LangChain is a Python framework for building agents and LLM-powered applications. It gives you one interface for talking to dozens of model providers, a production-ready agent loop (create_agent) built on LangGraph, a composable middleware system for retries, guardrails, human approval and context management, and the component library (messages, tools, prompts, output parsers, embeddings, vector stores, text splitters) that everything else is built from.
This guide covers the Python monorepo langchain-ai/langchain at commit a9780cd. It is organized by what you want to do: get a first result, install the right packages, build agents, add tools and memory, and fix things when they break. Architecture, internals, security and the complete configuration reference live in the companion Admin Guide.
| Package (PyPI) | Version in this snapshot | What it is |
|---|---|---|
langchain |
1.4.3 | The actively maintained high-level package: create_agent, middleware, init_chat_model, init_embeddings, MCP adapter |
langchain-core |
1.6.6 | Base abstractions: messages, Runnables, tools, prompts, output parsers, callbacks, vector store and retriever interfaces |
langchain-text-splitters |
1.1.2 | Document chunking utilities |
langchain-openai, langchain-anthropic, langchain-ollama, … |
per package | Provider integrations (16 live in this repo) |
langchain-classic |
1.0.8 | Legacy chains, AgentExecutor, memory classes — no new features |
langchain-tests |
1.1.9 | Standard test suites for integration authors |
langchain-model-profiles |
0.0.6 | Maintainer CLI (langchain-profiles) that generates model capability data |
Note
Every package requires Python 3.10 or newer (
requires-python = ">=3.10.0,<4.0.0"). LangChain has no server, no daemon and no configuration file of its own — it is a library you import. "Configuration" means constructor arguments, environment variables for provider credentials, and the per-callconfigdictionary.
1. Quick Start
The shortest path from zero to a working agent with a tool.
Step 1 — install the framework plus one provider package:
pip install -U langchain langchain-openai
Successfully installed langchain-1.4.3 langchain-core-1.6.6 langchain-openai-1.6.6 langgraph-1.2.x openai-2.x ...
Step 2 — set your provider key in the environment:
export OPENAI_API_KEY="sk-..."
Step 3 — talk to a model. init_chat_model takes a provider:model string and returns a ready chat model:
from langchain.chat_models import init_chat_model
model = init_chat_model("openai:gpt-5.5")
reply = model.invoke("Say hello in five words.")
print(reply.text)
Hello there, nice to meet!
Step 4 — build an agent. Any typed Python function with a docstring can be a tool:
from langchain.agents import create_agent
def get_weather(city: str) -> str:
"""Return the current weather for a city."""
return f"It's always sunny in {city}!"
agent = create_agent(
model="openai:gpt-5.5",
tools=[get_weather],
system_prompt="You are a helpful assistant.",
)
result = agent.invoke({"messages": [{"role": "user", "content": "What's the weather in Paris?"}]})
print(result["messages"][-1].text)
It's always sunny in Paris!
Tip
result["messages"]holds the whole conversation: yourHumanMessage, the model'sAIMessagewith a tool call, theToolMessageyour function produced, and the model's finalAIMessage. Print them all to see the agent loop at work.
Important
Model reply text varies from run to run. Outputs shown in this guide are representative, not exact.
- [x] Installed
langchainand a provider package - [x] Set the provider's API key
- [x] Got a model reply
- [x] Ran an agent that called a tool
2. Installation
2.1 Requirements
| Requirement | Value |
|---|---|
| Python | 3.10, 3.11, 3.12, 3.13 or 3.14 (>=3.10.0,<4.0.0) |
Core runtime dependencies of langchain |
langchain-core>=1.6.3,<2.0.0, langgraph>=1.2.11,<1.3.0, pydantic>=2.7.4,<3.0.0 |
Core runtime dependencies of langchain-core |
langsmith, httpx, tenacity, jsonpatch, PyYAML, typing-extensions, packaging, pydantic, uuid-utils, langchain-protocol |
| A model provider | An API key for a hosted provider, or a local runtime such as Ollama |
| OS | Any OS that runs CPython; a few optional features are POSIX-only (see HostExecutionPolicy limits in the Admin Guide) |
2.2 Choose what to install
LangChain is split into a small core plus one package per provider. Install the framework, then add only the providers you use.
uv (recommended by the project)
uv add langchain
uv add langchain-openai # or langchain-anthropic, langchain-ollama, ...
pip
pip install -U langchain
pip install -U langchain-openai
pip with extras
# The langchain package declares an extra per provider
pip install -U "langchain[openai,anthropic]"
MCP support
# langchain.mcp needs FastMCP 4.x
pip install -U "langchain[mcp]"
The langchain package declares these optional-dependency extras (each simply installs the named integration):
| Extra | Installs | Extra | Installs |
|---|---|---|---|
openai |
langchain-openai |
groq |
langchain-groq |
anthropic |
langchain-anthropic |
aws |
langchain-aws |
azure-ai |
langchain-azure-ai |
baseten |
langchain-baseten>=0.2.0 |
google-vertexai |
langchain-google-vertexai |
deepseek |
langchain-deepseek |
google-genai |
langchain-google-genai |
xai |
langchain-xai |
fireworks |
langchain-fireworks |
perplexity |
langchain-perplexity |
ollama |
langchain-ollama |
meta |
langchain-meta |
together |
langchain-together |
community |
langchain-community |
mistralai |
langchain-mistralai |
mcp |
fastmcp>=4.0.1,<5.0.0 |
huggingface |
langchain-huggingface |
Note
Only 16 provider packages live in this repository (Anthropic, Chroma, DeepSeek, Exa, Fireworks, Groq, Hugging Face, Mistral AI, Nomic, Ollama, OpenAI, OpenRouter, Perplexity, Qdrant, TypeSafe, xAI). Google, AWS, Azure AI, Cohere and many others are maintained in their own repositories but are still reachable through
init_chat_modelonce installed.
2.3 Provider packages in this repository
| Package | Version | Main classes | Extra dependencies it pulls in |
|---|---|---|---|
langchain-openai |
1.6.6 | ChatOpenAI, AzureChatOpenAI, OpenAIEmbeddings, AzureOpenAIEmbeddings, OpenAI, AzureOpenAI |
openai>=2.45.0,<4.0.0, tiktoken; extra azure-identity |
langchain-anthropic |
1.7.5 | ChatAnthropic, AnthropicLLM, convert_to_anthropic_tool |
anthropic>=0.120.0,<2.0.0 |
langchain-ollama |
1.1.0 | ChatOllama, OllamaEmbeddings, OllamaLLM |
ollama>=0.6.1,<1.0.0 |
langchain-groq |
1.1.3 | ChatGroq |
groq>=0.30.0,<1.0.0 |
langchain-mistralai |
1.1.6 | ChatMistralAI, MistralAIEmbeddings |
tokenizers, httpx, httpx-sse |
langchain-fireworks |
1.7.0 | ChatFireworks, Fireworks, FireworksEmbeddings, FireworksRerank |
fireworks-ai, openai, aiohttp |
langchain-xai |
1.3.0 | ChatXAI |
langchain-openai |
langchain-deepseek |
1.1.1 | ChatDeepSeek |
langchain-openai |
langchain-openrouter |
0.2.9 | ChatOpenRouter |
openrouter>=0.9.2,<1.0.0 |
langchain-perplexity |
1.4.1 | ChatPerplexity, PerplexityEmbeddings, PerplexitySearchRetriever, PerplexitySearchResults |
perplexityai |
langchain-huggingface |
1.2.2 | ChatHuggingFace, HuggingFaceEndpoint, HuggingFacePipeline, HuggingFaceEmbeddings, HuggingFaceEndpointEmbeddings |
huggingface-hub, tokenizers; extra full adds transformers + sentence-transformers |
langchain-nomic |
1.0.1 | NomicEmbeddings |
nomic, pillow |
langchain-exa |
1.1.0 | ExaSearchRetriever, ExaSearchResults, ExaFindSimilarResults |
exa-py |
langchain-chroma |
1.1.0 | Chroma |
chromadb>=1.5.5,<2.0.0 |
langchain-qdrant |
1.1.0 | QdrantVectorStore, FastEmbedSparse, legacy Qdrant |
qdrant-client; extra fastembed |
langchain-typesafe |
0.0.1a3 (alpha) | TypeSafeClassifier |
httpx2; extra experimental |
2.4 Text splitters and optional helpers
pip install -U langchain-text-splitters
# Optional back ends are NOT declared as extras; install what you use:
pip install tiktoken # TokenTextSplitter, from_tiktoken_encoder
pip install beautifulsoup4 lxml # HTML splitters
pip install nltk spacy # sentence splitters
2.5 Verify the installation
langchain-core ships a system report you can paste into bug reports:
python -m langchain_core.sys_info
System Information
------------------
> OS: Linux
> OS Version: ...
> Python Version: 3.12.x
Package Information
-------------------
> langchain_core: 1.6.6
> langchain: 1.4.3
> langsmith: 0.x
> langchain_openai: 1.6.6
> langgraph: 1.2.x
...
Tip
print_sys_info(additional_pkgs=[...])is also importable fromlangchain_core.sys_infoif you want to include extra packages in the report.
2.6 Upgrading from LangChain 0.x
LangChain 1.0 moved legacy functionality into langchain-classic. If an old import stops working, it has usually moved:
| Old usage | Where it lives now |
|---|---|
AgentExecutor, initialize_agent, agent types |
langchain_classic.agents (deprecated; use langchain.agents.create_agent) |
ConversationBufferMemory and other memory classes |
langchain_classic.memory (deprecated since 0.3.1; use a checkpointer with create_agent) |
LLMChain, RetrievalQA, APIChain, SQL chains |
langchain_classic.chains |
CacheBackedEmbeddings, community embeddings |
langchain_classic / langchain-community |
langchain.hub.pull / push |
langchain_classic.hub (deprecated since 1.0.6; use the LangSmith SDK) |
pip install -U langchain-classic
Warning
langchain-classicreceives no new features. Its README says: "In most cases, you should be using the mainlangchainpackage."
3. Configuration Basics
LangChain reads no config file. You configure it in three places: environment variables (mostly credentials), constructor arguments on models and middleware, and the per-call config dict. The exhaustive reference is in the Admin Guide, Section 4.
3.1 Environment Variables
Provider packages read their credentials from the environment when you do not pass api_key=...:
| Provider | API key | Base URL override | Other |
|---|---|---|---|
OpenAI (ChatOpenAI) |
OPENAI_API_KEY |
OPENAI_API_BASE (LangChain), OPENAI_BASE_URL (OpenAI SDK) |
OPENAI_ORG_ID / OPENAI_ORGANIZATION, OPENAI_PROXY |
Azure OpenAI (AzureChatOpenAI) |
AZURE_OPENAI_API_KEY (falls back to OPENAI_API_KEY) |
AZURE_OPENAI_ENDPOINT |
OPENAI_API_VERSION (required — no default for chat), AZURE_OPENAI_AD_TOKEN |
Anthropic (ChatAnthropic) |
ANTHROPIC_API_KEY |
ANTHROPIC_API_URL, then ANTHROPIC_BASE_URL |
ANTHROPIC_PROXY |
| Groq | GROQ_API_KEY |
GROQ_API_BASE |
GROQ_PROXY |
| Mistral AI | MISTRAL_API_KEY |
MISTRAL_BASE_URL (chat only) |
— |
| xAI | XAI_API_KEY |
XAI_API_BASE |
— |
| DeepSeek | DEEPSEEK_API_KEY |
DEEPSEEK_API_BASE |
— |
| Fireworks | FIREWORKS_API_KEY |
FIREWORKS_API_BASE |
— |
| OpenRouter | OPENROUTER_API_KEY |
OPENROUTER_API_BASE |
OPENROUTER_APP_URL, OPENROUTER_APP_TITLE, OPENROUTER_SESSION_ID |
| Perplexity | PPLX_API_KEY (chat); PPLX_API_KEY or PERPLEXITY_API_KEY (embeddings, retriever, tool) |
— | — |
| Hugging Face | HUGGINGFACEHUB_API_TOKEN, falling back to HF_TOKEN |
HF_INFERENCE_ENDPOINT |
— |
| Nomic | NOMIC_API_KEY |
— | — |
| Exa | EXA_API_KEY |
— | — |
| Ollama | none | none — pass base_url= (default comes from the ollama SDK) |
— |
Framework-level variables:
| Variable | Effect |
|---|---|
LANGSMITH_TRACING=true + LANGSMITH_API_KEY |
Sends traces of every run to LangSmith (read by the langsmith SDK) |
LANGSMITH_PROJECT |
LangSmith project name for traces |
LC_OUTPUT_VERSION |
Default output_version for chat models: v0 (provider-native content) or v1 (standard content blocks) |
LANGSMITH_GATEWAY |
true/1/yes routes OpenAI, Anthropic and Fireworks through the LangSmith gateway; any other non-false value is used as a custom gateway URL |
Caution
Setting
LANGCHAIN_TRACINGorLANGCHAIN_HANDLER(the old v1 tracer variables) now raisesRuntimeError: Tracing using LangChainTracerV1 is no longer supported.... Unset them and useLANGSMITH_TRACING.
Warning
ChatOllamadoes not readOLLAMA_HOST. Passbase_url="http://host:11434"explicitly if your Ollama server is not at the SDK's default address.
3.2 Choosing a model with a string
Everything that accepts a model (create_agent, ModelFallbackMiddleware, SummarizationMiddleware, …) accepts either a chat model object or a "provider:model" string. Strings are resolved by init_chat_model:
from langchain.chat_models import init_chat_model
gpt = init_chat_model("openai:gpt-5.5", temperature=0)
claude = init_chat_model("anthropic:claude-sonnet-4-5-20250929", max_tokens=1024)
local = init_chat_model("ollama:llama3.1", base_url="http://localhost:11434")
Any extra keyword argument is passed to the provider class constructor, so temperature, max_tokens, timeout, max_retries, base_url and rate_limiter all work.
Tip
Prefer the prefixed form (
"anthropic:..."). A bare name such as"claude-opus-4-7"also works because the provider is inferred from the prefix, but inference is best-effort (see Section 10.2 for the exact rules), and names likeo4-miniare not inferred.
3.3 Per-call configuration
Every invoke, stream and batch call accepts a config dict (RunnableConfig):
| Key | Purpose |
|---|---|
configurable |
Runtime parameters, e.g. {"thread_id": "user-42"} for agent memory or {"model": "..."} for configurable models |
tags, metadata |
Attached to traces and callbacks |
callbacks |
Callback handlers for this call |
run_name, run_id |
Name / ID for the trace of this call |
max_concurrency |
Upper bound on parallel work in batch and parallel steps |
recursion_limit |
Max nested runnable depth (default 25). Agents built with create_agent set their own graph limit of 9,999 |
config = {"configurable": {"thread_id": "session-1"}, "tags": ["prod"], "metadata": {"user": "u-17"}}
agent.invoke({"messages": [{"role": "user", "content": "hi"}]}, config)
Note
RunnableConfighas notimeoutkey. Unknown top-level keys are silently moved intoconfigurable. Set timeouts on the model (timeout=/request_timeout=).
4. Everyday Usage
4.1 Talking to a chat model
Every chat model implements the same Runnable interface: invoke, stream, batch and their async versions ainvoke, astream, abatch.
Inputs can be a string, a list of messages, or message-like dicts and tuples:
from langchain.messages import HumanMessage, SystemMessage
model.invoke("What is 2 + 2?")
model.invoke([SystemMessage("Answer tersely."), HumanMessage("What is 2 + 2?")])
model.invoke([{"role": "system", "content": "Answer tersely."}, {"role": "user", "content": "What is 2 + 2?"}])
model.invoke([("system", "Answer tersely."), ("human", "What is 2 + 2?")])
Accepted role strings: human/user, ai/assistant, system, developer (becomes a SystemMessage flagged for OpenAI's developer role), tool, function and remove.
Outputs are AIMessage objects:
msg = model.invoke("Name one planet.")
print(msg.text) # plain text of the reply
print(msg.content_blocks) # standardized content blocks
print(msg.usage_metadata) # token usage
print(msg.response_metadata.get("model_name"))
Mars.
[{'type': 'text', 'text': 'Mars.'}]
{'input_tokens': 11, 'output_tokens': 3, 'total_tokens': 14}
gpt-5.5
Note
Use
.textas a property. Calling.text()as a method is deprecated since 1.0.0 and will be removed in 2.0.0.
Streaming yields AIMessageChunk objects that you can add together:
full = None
for chunk in model.stream("Write a haiku about rivers."):
print(chunk.text, end="", flush=True)
full = chunk if full is None else full + chunk
print()
print(full.usage_metadata)
Silver threads descend
carving patience into stone
the sea waits below
{'input_tokens': 14, 'output_tokens': 18, 'total_tokens': 32}
Batching runs many inputs in a thread pool:
answers = model.batch(["Capital of France?", "Capital of Japan?"], config={"max_concurrency": 4})
print([a.text for a in answers])
['Paris.', 'Tokyo.']
Tip
Use
return_exceptions=Trueinbatchto get exceptions back in place of results instead of failing the whole batch. It catchesExceptionsubclasses only.
Note
If a provider class does not implement token streaming,
stream()still works — it falls back toinvoke()and yields one chunk.
4.2 Building an agent
create_agent returns a compiled LangGraph graph that calls the model, runs any tool calls, feeds results back and repeats until the model answers without tool calls.
from langchain.agents import create_agent
agent = create_agent(
model="anthropic:claude-sonnet-4-5-20250929",
tools=[get_weather],
system_prompt="You are a concise travel assistant.",
name="travel_agent",
)
The key parameters (full list in Section 10.3):
| Parameter | Type | What it does |
|---|---|---|
model |
str or BaseChatModel |
The model; strings go through init_chat_model |
tools |
list of BaseTool, callables, or provider tool dicts |
Tools the model may call. None or [] gives a model-only agent |
system_prompt |
str or SystemMessage |
Prepended to every model call |
middleware |
sequence of AgentMiddleware |
Hooks around the loop (Section 6) |
response_format |
Pydantic class, dataclass, TypedDict, JSON-schema dict, ToolStrategy, ProviderStrategy |
Structured final answer (Section 4.4) |
checkpointer |
LangGraph checkpointer | Enables conversation memory and resumable interrupts |
store |
LangGraph BaseStore |
Cross-thread storage |
context_schema |
type | Type of the per-run context object passed at invoke time |
name |
str |
Graph name; also stamped onto the agent's AIMessage.name |
Invoking. The input is a dict with a messages list; the output is the final state:
state = agent.invoke({"messages": [{"role": "user", "content": "Weather in Rome?"}]})
for m in state["messages"]:
print(type(m).__name__, "->", m.text or m.tool_calls)
HumanMessage -> Weather in Rome?
AIMessage -> [{'name': 'get_weather', 'args': {'city': 'Rome'}, 'id': 'toolu_01...', 'type': 'tool_call'}]
ToolMessage -> It's always sunny in Rome!
AIMessage -> It's sunny in Rome right now.
Streaming an agent. The compiled graph supports LangGraph stream modes. The two most useful:
# Node-by-node progress
for update in agent.stream({"messages": [{"role": "user", "content": "Weather in Oslo?"}]}, stream_mode="updates"):
print(update)
# Token-by-token model output
for token, metadata in agent.stream({"messages": [{"role": "user", "content": "Weather in Oslo?"}]}, stream_mode="messages"):
print(token.text, end="")
{'model': {'messages': [AIMessage(content='', tool_calls=[{'name': 'get_weather', ...}])]}}
{'tools': {'messages': [ToolMessage(content="It's always sunny in Oslo!", ...)]}}
{'model': {'messages': [AIMessage(content="It's sunny in Oslo.")]}}
Tip
Middleware can emit your own progress events with
runtime.stream_writer({...}); receive them withstream_mode="custom".
Note
The agent graph's
recursion_limitis set to 9,999 bycreate_agent, so long tool loops are not cut off at LangGraph's usual limit. UseModelCallLimitMiddlewareorToolCallLimitMiddlewareto cap loops deliberately.
4.3 Defining tools
Plain functions work directly — the name comes from the function, the description from the docstring, the schema from type hints. For more control use the @tool decorator:
from typing import Annotated
from langchain.tools import tool
@tool(parse_docstring=True)
def search_orders(customer_id: str, limit: int = 5) -> str:
"""Look up recent orders for a customer.
Args:
customer_id: The customer's ID, e.g. "C-1001".
limit: Maximum number of orders to return.
"""
return f"{limit} orders for {customer_id}"
print(search_orders.name, search_orders.args)
search_orders {'customer_id': {'description': 'The customer\'s ID, e.g. "C-1001".', 'title': 'Customer Id', 'type': 'string'}, 'limit': {'default': 5, 'description': 'Maximum number of orders to return.', 'title': 'Limit', 'type': 'integer'}}
@tool options:
| Option | Default | Meaning |
|---|---|---|
| first positional arg | function name | Custom tool name: @tool("lookup") |
description |
docstring | Overrides the description |
return_direct |
False |
End the agent loop and return the tool output directly |
args_schema |
inferred | Pydantic model or JSON-schema dict for arguments |
infer_schema |
True |
Infer the schema from the signature |
response_format |
"content" |
"content_and_artifact" means the tool returns (content, artifact); the artifact is kept on the ToolMessage but not sent to the model |
parse_docstring |
False |
Read argument descriptions from a Google-style Args: section |
error_on_invalid_docstring |
True |
Raise if parse_docstring=True and the docstring is malformed |
extras |
None |
Provider-specific fields (e.g. Anthropic cache_control, defer_loading) |
Warning
Do not name tool arguments
config,run_managerorcallbacks.configis shadowed by the injectedRunnableConfig, andrun_manager/callbacksare silently removed from the schema.
Injected arguments are supplied by the framework and hidden from the model:
from typing import Annotated
from langchain.tools import tool, ToolRuntime, InjectedToolCallId
@tool
def whoami(runtime: ToolRuntime) -> str:
"""Report who the current user is."""
return f"user={runtime.context['user_id']}, messages so far={len(runtime.state['messages'])}"
@tool
def echo_id(text: str, tool_call_id: Annotated[str, InjectedToolCallId]) -> str:
"""Echo text with the calling tool-call ID."""
return f"{text} ({tool_call_id})"
ToolRuntime (re-exported by langchain.tools from LangGraph) gives a tool access to state, context, config, store, stream_writer and tool_call_id.
Errors inside tools. Raise ToolException to report a failure the model can react to, and set handle_tool_error on the tool:
from langchain.tools import ToolException, tool
@tool
def divide(a: float, b: float) -> float:
"""Divide a by b."""
if b == 0:
raise ToolException("Cannot divide by zero; ask the user for a non-zero divisor.")
return a / b
divide.handle_tool_error = True # the exception message becomes the ToolMessage content
handle_tool_error value |
Behavior |
|---|---|
False (default) |
The exception propagates and stops the run |
True |
The exception message becomes an error ToolMessage (or "Tool execution error" if empty) |
| a string | That string becomes the error ToolMessage |
| a callable | Its return value becomes the error ToolMessage |
Tip
For agent-wide policies (retry flaky tools, convert only certain exceptions to messages) use
ToolRetryMiddlewareandToolErrorMiddlewareinstead of per-tool settings — see Section 6.1.
Provider-native tools (web search, code execution, etc.) are passed as dicts and executed by the provider, not locally:
agent = create_agent("openai:gpt-5.5", tools=[{"type": "web_search"}, get_weather])
4.4 Getting structured output
From a model — with_structured_output returns a runnable that produces your schema:
from pydantic import BaseModel, Field
class Movie(BaseModel):
"""A movie recommendation."""
title: str
year: int = Field(description="Release year")
structured = init_chat_model("openai:gpt-5.5").with_structured_output(Movie)
print(structured.invoke("Recommend one classic sci-fi film."))
title='Blade Runner' year=1982
Pass include_raw=True to get {"raw": AIMessage, "parsed": Movie | None, "parsing_error": Exception | None} instead of raising on parse failures.
From an agent — pass response_format to create_agent; the parsed object appears in result["structured_response"]:
agent = create_agent("openai:gpt-5.5", tools=[get_weather], response_format=Movie)
result = agent.invoke({"messages": [{"role": "user", "content": "Recommend a film set in Paris."}]})
print(result["structured_response"])
title='Midnight in Paris' year=2011
How the strategy is chosen:
| You pass | What happens |
|---|---|
| A raw schema (Pydantic, dataclass, TypedDict, JSON-schema dict) | Auto: provider-native structured output if the model supports it (from its profile, or a built-in list of known models), otherwise the tool-calling strategy |
ToolStrategy(schema, handle_errors=True, tool_message_content=None) |
The schema is exposed as a tool and the model is forced to call a tool (tool_choice="any"). Parse errors are fed back to the model with "Error: {error}\n Please fix your mistakes." and it retries |
ProviderStrategy(schema, strict=None) |
Uses the provider's native JSON-schema output. Parse failures raise StructuredOutputValidationError |
from langchain.agents.structured_output import ToolStrategy, ProviderStrategy
agent = create_agent("openai:gpt-5.5", tools=[], response_format=ToolStrategy(Movie, handle_errors=True))
Tip
ToolStrategyaccepts a union of schemas (ToolStrategy(Movie | Book)); the model picks one. If it calls two at once you getMultipleStructuredOutputsError, whichhandle_errors=Trueturns into a retry.
4.5 Conversation memory
An agent remembers a conversation only when it has a checkpointer and you reuse the same thread_id:
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent("openai:gpt-5.5", tools=[get_weather], checkpointer=InMemorySaver())
cfg = {"configurable": {"thread_id": "alice"}}
agent.invoke({"messages": [{"role": "user", "content": "My name is Alice."}]}, cfg)
reply = agent.invoke({"messages": [{"role": "user", "content": "What's my name?"}]}, cfg)
print(reply["messages"][-1].text)
Your name is Alice.
Important
InMemorySaverloses everything when the process exits. For durable memory use a persistent LangGraph checkpointer (for example a Postgres or SQLite saver from the LangGraph ecosystem).
Keeping the context window under control:
| Tool | Where | What it does |
|---|---|---|
trim_messages(messages, max_tokens=..., token_counter=..., strategy="last", start_on="human", include_system=True) |
langchain.messages |
Keep only the most recent messages that fit a budget |
SummarizationMiddleware(model, trigger=..., keep=...) |
langchain.agents.middleware |
Replace older history with a model-written summary when a threshold is crossed |
ContextEditingMiddleware(edits=[ClearToolUsesEdit(...)]) |
langchain.agents.middleware |
Replace old tool outputs with [cleared] once input tokens exceed a trigger (default 100,000) |
from langchain.agents.middleware import SummarizationMiddleware
agent = create_agent(
"openai:gpt-5.5",
tools=[get_weather],
checkpointer=InMemorySaver(),
middleware=[SummarizationMiddleware("openai:gpt-5.4-mini", trigger=("tokens", 4000), keep=("messages", 20))],
)
Warning
SummarizationMiddlewarehas no default trigger. If you omittrigger, it never summarizes. Always set one, e.g.trigger=("tokens", 4000)ortrigger=[("fraction", 0.8), ("messages", 100)].
4.6 Prompts, chains and parsers
For deterministic pipelines that do not need an agent loop, compose components with the | operator (LangChain Expression Language):
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_messages([
("system", "You translate English to {language}."),
("human", "{text}"),
])
chain = prompt | init_chat_model("openai:gpt-5.5") | StrOutputParser()
print(chain.invoke({"language": "French", "text": "Good morning"}))
Bonjour
| Building block | Import | Notes |
|---|---|---|
ChatPromptTemplate.from_messages([...]) |
langchain_core.prompts |
Messages as (role, template) tuples, strings, message objects or {"role","content"} dicts |
MessagesPlaceholder("history", optional=True) |
langchain_core.prompts |
Slot for a list of messages; ("placeholder", "{history}") is shorthand and optional by default |
PromptTemplate.from_template("...") |
langchain_core.prompts |
Single-string prompts; template_format is f-string (default), mustache or jinja2 |
StrOutputParser, JsonOutputParser, PydanticOutputParser, XMLOutputParser, list parsers |
langchain_core.output_parsers |
Turn model output into Python values |
RunnableParallel / a plain dict |
langchain_core.runnables |
Run steps side by side: {"a": chain1, "b": chain2} |
RunnablePassthrough.assign(key=...) |
langchain_core.runnables |
Add computed keys to a dict input |
RunnableLambda(fn) / any function |
langchain_core.runnables |
Wrap a function as a step |
.with_retry(stop_after_attempt=3), .with_fallbacks([...]) |
any runnable | Resilience on a single step |
Caution
Never accept
jinja2templates from untrusted users. The docs in the source state that jinja2 sandboxing "should be treated as a best-effort approach rather than a guarantee of security". Preferf-stringormustache.
4.7 Embeddings, text splitting and retrieval
from langchain.embeddings import init_embeddings
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
embeddings = init_embeddings("openai:text-embedding-3-small")
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents([Document(page_content=open("handbook.txt").read(), metadata={"source": "handbook"})])
store = InMemoryVectorStore(embeddings)
store.add_documents(chunks)
for doc in store.similarity_search("vacation policy", k=2):
print(doc.metadata["source"], doc.page_content[:60])
handbook Employees accrue 1.5 days of paid vacation per month...
handbook Vacation requests must be submitted two weeks in advance...
init_embeddings requires the provider:model form (or provider=) and supports these providers: openai, azure_openai, azure_ai, bedrock, cohere, google_genai, google_vertexai, huggingface, mistralai, ollama.
Turn the store into a retriever and then into a tool an agent can call:
from langchain_core.tools import create_retriever_tool
retriever = store.as_retriever(search_type="mmr", search_kwargs={"k": 4, "fetch_k": 20})
handbook_tool = create_retriever_tool(retriever, "search_handbook", "Search the employee handbook.")
agent = create_agent("openai:gpt-5.5", tools=[handbook_tool])
search_type |
search_kwargs |
|---|---|
"similarity" (default) |
k (default 4), filter |
"mmr" |
k, fetch_k (default 20), lambda_mult (default 0.5; 1 = least diverse) |
"similarity_score_threshold" |
k, score_threshold — must be a float (e.g. 0.5, not 1) |
Note
InMemoryVectorStorefilters are Python callables (filter=lambda doc: doc.metadata["source"] == "handbook"), not metadata dicts. It needsnumpyfor similarity math, and can be saved withstore.dump(path)and reloaded withInMemoryVectorStore.load(path, embeddings).
5. Common Workflows
Each recipe is complete and runnable once the named provider package is installed and its API key is set.
5.1 A support agent with memory and per-user context
from dataclasses import dataclass
from langchain.agents import create_agent
from langchain.agents.middleware import dynamic_prompt, ModelRequest
from langchain.tools import tool, ToolRuntime
from langgraph.checkpoint.memory import InMemorySaver
@dataclass
class UserContext:
user_id: str
plan: str
@tool
def account_status(runtime: ToolRuntime) -> str:
"""Return the caller's account status."""
ctx = runtime.context
return f"User {ctx.user_id} is on the {ctx.plan} plan and in good standing."
@dynamic_prompt
def plan_aware_prompt(request: ModelRequest) -> str:
return f"You are a support agent. The customer is on the {request.runtime.context.plan} plan."
agent = create_agent(
"openai:gpt-5.5",
tools=[account_status],
middleware=[plan_aware_prompt],
context_schema=UserContext,
checkpointer=InMemorySaver(),
)
cfg = {"configurable": {"thread_id": "ticket-881"}}
ctx = UserContext(user_id="u-17", plan="Pro")
out = agent.invoke({"messages": [{"role": "user", "content": "Is my account OK?"}]}, cfg, context=ctx)
print(out["messages"][-1].text)
Yes — your account (u-17) is on the Pro plan and in good standing.
Tip
Context is per-run, read-only information you supply (
context=). State is the conversation data the graph accumulates and the checkpointer saves. Keep secrets and user identity in context, not in the prompt.
5.2 Human approval before risky tool calls
HumanInTheLoopMiddleware pauses the run with a LangGraph interrupt whenever the model calls a gated tool. Resuming requires a checkpointer.
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langchain.tools import tool
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command
@tool
def delete_file(path: str) -> str:
"""Delete a file."""
return f"Deleted {path}"
@tool
def read_file(path: str) -> str:
"""Read a file."""
return f"Contents of {path}"
agent = create_agent(
"openai:gpt-5.5",
tools=[delete_file, read_file],
middleware=[HumanInTheLoopMiddleware(interrupt_on={
"delete_file": {"allowed_decisions": ["approve", "edit", "reject"]},
"read_file": False, # auto-approved
})],
checkpointer=InMemorySaver(),
)
cfg = {"configurable": {"thread_id": "cleanup-1"}}
paused = agent.invoke({"messages": [{"role": "user", "content": "Delete report.md"}]}, cfg)
[interrupt] = paused["__interrupt__"]
print(interrupt.value["action_requests"][0]["description"])
final = agent.invoke(Command(resume={"decisions": [{"type": "approve"}]}), cfg)
print(final["messages"][-1].text)
Tool execution requires approval
Tool: delete_file
Args: {'path': 'report.md'}
I've deleted report.md.
Decision types you can send back, one per interrupted tool call and in the same order:
| Decision | Shape | Effect |
|---|---|---|
| approve | {"type": "approve"} |
Run the tool as proposed |
| edit | {"type": "edit", "edited_action": {"name": "delete_file", "args": {"path": "draft.md"}}} |
Run the reviewer's version instead; the result is prefixed with a notice telling the model a human replaced the call |
| reject | {"type": "reject", "message": "Not allowed"} |
Skip the tool; an error ToolMessage tells the model not to retry |
| respond | {"type": "respond", "message": "The file is already gone."} |
Skip the tool and answer on its behalf with a successful ToolMessage |
Important
The number of decisions must equal the number of interrupted tool calls, or you get
ValueError: Number of human decisions (...) does not match number of hanging tool calls (...).
Tip
Use
InterruptOnConfig(allowed_decisions=[...], description=callable, when=predicate)to build custom approval prompts and to interrupt only some calls (for example only whenargs["amount"] > 1000). Thedescriptioncallable receives(tool_call, state, runtime).
5.3 Giving an agent tools from MCP servers
import asyncio
from langchain.agents import create_agent
from langchain.mcp import MCPAdapter
async def main() -> None:
async with MCPAdapter("https://example.com/mcp") as adapter:
tools = await adapter.list_tools()
agent = create_agent("anthropic:claude-sonnet-4-5-20250929", tools)
result = await agent.ainvoke({"messages": [{"role": "user", "content": "What can you do?"}]})
print(result["messages"][-1].text)
asyncio.run(main())
Several servers behind one adapter, mixing transports:
import sys
config = {
"mcpServers": {
"weather": {"command": sys.executable, "args": ["weather_server.py"]},
"docs": {"url": "https://docs.example.com/mcp"},
}
}
async with MCPAdapter(config) as adapter:
tools = await adapter.list_tools() # names are prefixed per server
| Target you pass | Transport |
|---|---|
"https://..." string |
Streamable HTTP. Only http/https strings are accepted |
pathlib.Path("server.py") |
Launches the script over stdio |
fastmcp.FastMCP / MCPServer instance |
In-process (ideal for tests) |
MCPConfig or {"mcpServers": {...}} dict |
Several servers, tools prefixed with the server key |
fastmcp.Client(...) |
Pre-built client (e.g. with auth="token") |
Warning
langchain.mcpis beta. Importing it emitsLangChainBetaWarning:langchain.mcpis in beta.... Silence it withwarnings.filterwarnings("ignore", category=LangChainBetaWarning). MCP tools are async-only, so call the agent withainvoke/astream.
Note
MCP tools that fail on the server reach the model as an error
ToolMessageso it can retry. When a server asks the user a question mid-call (MCP elicitation), the run pauses with an interrupt of typemcp_elicitation; resume withCommand(resume={"responses": {request_key: {"action": "accept", "content": {...}}}}). This also needs a checkpointer.
5.4 Retrieval-augmented agent
from langchain.agents import create_agent
from langchain.embeddings import init_embeddings
from langchain_core.documents import Document
from langchain_core.tools import create_retriever_tool
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_text_splitters import RecursiveCharacterTextSplitter
docs = [Document(page_content=open(p).read(), metadata={"source": p}) for p in ["faq.md", "pricing.md"]]
chunks = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=100).split_documents(docs)
store = InMemoryVectorStore(init_embeddings("openai:text-embedding-3-small"))
store.add_documents(chunks)
kb = create_retriever_tool(
store.as_retriever(search_kwargs={"k": 4}),
name="search_kb",
description="Search the product FAQ and pricing pages.",
)
agent = create_agent("openai:gpt-5.5", tools=[kb], system_prompt="Answer only from search_kb results; cite the source.")
print(agent.invoke({"messages": [{"role": "user", "content": "Is there a student discount?"}]})["messages"][-1].text)
Yes — students get 50% off the Pro plan (source: pricing.md).
Tip
For a persistent store swap
InMemoryVectorStoreforlangchain_chroma.Chroma(collection_name="kb", embedding_function=..., persist_directory="./chroma")orlangchain_qdrant.QdrantVectorStore.
5.5 Guardrails: PII redaction and call limits
from langchain.agents import create_agent
from langchain.agents.middleware import (
ModelCallLimitMiddleware,
PIIMiddleware,
ToolCallLimitMiddleware,
)
agent = create_agent(
"openai:gpt-5.5",
tools=[search_orders],
middleware=[
PIIMiddleware("email", strategy="redact"), # user input
PIIMiddleware("credit_card", strategy="mask", apply_to_output=True),
PIIMiddleware("api_key", detector=r"sk-[a-zA-Z0-9]{32}", strategy="block"),
ModelCallLimitMiddleware(run_limit=10, exit_behavior="end"),
ToolCallLimitMiddleware(tool_name="search_orders", run_limit=3),
],
)
| PII strategy | Result for [email removed] / card 4111111111111111 |
|---|---|
redact |
[REDACTED_EMAIL] / [REDACTED_CREDIT_CARD] |
mask |
jane@****.com / ************1111 (a card written with dashes keeps them: ****-****-****-1111) |
hash |
<email_hash:1a2b3c4d> — first 8 hex characters of a SHA-256 of the value |
block |
raises PIIDetectionError |
Built-in detectors: email, credit_card (Luhn-validated), ip, mac_address, url. Anything else needs detector= (a regex string or a function returning PIIMatch dicts).
Note
PIIMiddlewarechecks user input by default (apply_to_input=True). Setapply_to_output=Trueto also scrub AI messages — this additionally installs a stream transformer so streamed tokens and tool events are redacted in flight — andapply_to_tool_results=Trueto scrub tool outputs.
5.6 Resilience: retries and model fallback
from langchain.agents.middleware import ModelFallbackMiddleware, ModelRetryMiddleware, ToolRetryMiddleware
agent = create_agent(
"openai:gpt-5.5",
tools=[search_orders],
middleware=[
ModelFallbackMiddleware("anthropic:claude-sonnet-4-5-20250929", "ollama:llama3.1"),
ModelRetryMiddleware(max_retries=3, backoff_factor=2.0, initial_delay=1.0),
ToolRetryMiddleware(max_retries=2, tools=["search_orders"], on_failure="continue"),
],
)
Retry delay is initial_delay * backoff_factor ** retry_number, capped at max_delay (default 60 s), with ±25% jitter by default. After retries are exhausted, on_failure="continue" returns an error message to the model (AIMessage for model retries, ToolMessage for tool retries), "error" re-raises, and a callable lets you format the message.
Important
Middleware order matters: the first middleware in the list is the outermost wrapper. Above, a failing model call is retried 3 times by
ModelRetryMiddlewarebeforeModelFallbackMiddlewaremoves on to the next model.
Warning
By default
ToolRetryMiddlewareretries any exception (GitHub issue #40688 notes this can repeat non-idempotent tool calls after ambiguous transport errors). For tools that change data, passretry_on=(TimeoutError, ConnectionError)or restrict withtools=[...].
5.7 Switching models at runtime
from langchain.chat_models import init_chat_model
model = init_chat_model("openai:gpt-5.5", configurable_fields=("model", "model_provider", "temperature"), temperature=0)
model.invoke("hi") # uses gpt-5.5
model.invoke("hi", config={"configurable": {"model": "anthropic:claude-sonnet-4-5-20250929", "temperature": 0.7}})
With config_prefix="writer", the keys become writer_model, writer_temperature and so on, which lets two configurable models live in one app.
Caution
configurable_fields="any"lets callers changeapi_keyandbase_urlat runtime, potentially redirecting requests to another service. When config comes from untrusted input, list the allowed fields explicitly.
5.8 Planning long tasks with a todo list
from langchain.agents.middleware import TodoListMiddleware
agent = create_agent("openai:gpt-5.5", tools=[read_file], middleware=[TodoListMiddleware()])
result = agent.invoke({"messages": [{"role": "user", "content": "Review these five files and summarize risks."}]})
print(result["todos"])
[{'content': 'Read file 1', 'status': 'completed'}, {'content': 'Read file 2', 'status': 'in_progress'}, ...]
The middleware adds a write_todos tool plus a system-prompt section explaining when to use it, stores the list in state under todos, and allows at most one write_todos call per model turn.
6. Advanced Usage
6.1 Plugins & Extensions: the middleware catalog
Middleware is LangChain's extension mechanism for agents. Everything below is importable from langchain.agents.middleware.
| Middleware | What it does | Key arguments (defaults) |
|---|---|---|
ModelRetryMiddleware |
Retry failed model calls with backoff | max_retries=2, retry_on=default_retry_on, on_failure="continue", backoff_factor=2.0, initial_delay=1.0, max_delay=60.0, jitter=True |
ToolRetryMiddleware |
Retry failed tool calls | same as above plus tools=None (all tools) |
ModelFallbackMiddleware |
Try alternative models in order on error | first_model, *additional_models |
ModelCallLimitMiddleware |
Cap model calls per thread/run | thread_limit=None, run_limit=None, exit_behavior="end" (or "error") |
ToolCallLimitMiddleware |
Cap tool calls (all or one tool) | tool_name=None, thread_limit, run_limit, exit_behavior="continue" ("error", "end") |
ToolErrorMiddleware |
Convert selected tool exceptions into error ToolMessages |
on_error, aon_error, tools=None |
HumanInTheLoopMiddleware |
Pause for human approve/edit/reject/respond | interrupt_on, description_prefix="Tool execution requires approval", edit_notice |
PIIMiddleware |
Detect and redact/mask/hash/block PII | pii_type, strategy="redact", detector=None, apply_to_input=True, apply_to_output=False, apply_to_tool_results=False |
SummarizationMiddleware |
Summarize old history | model, trigger=None, keep=("messages", 20), token_counter, summary_prompt, trim_tokens_to_summarize=4000 |
ContextEditingMiddleware |
Clear old tool outputs over a token threshold | edits=[ClearToolUsesEdit()], token_count_method="approximate", token_counter=None |
TodoListMiddleware |
write_todos planning tool |
system_prompt, tool_description |
LLMToolSelectorMiddleware |
Let a (smaller) model pick which tools to expose | model=None, max_tools=None, always_include=None, max_retries=1, on_parsing_failure="error" |
ProviderToolSearchMiddleware |
Defer tools behind the provider's server-side tool search (Anthropic, OpenAI) | searchable_tools=None |
LLMToolEmulator |
Fake tool results with an LLM (testing) | tools=None (all), model=None (deprecated default anthropic:claude-sonnet-4-5-20250929) |
ShellToolMiddleware |
Persistent shell tool | workspace_root, execution_policy=HostExecutionPolicy(), redaction_rules, tool_name="shell", env |
FilesystemFileSearchMiddleware |
Glob and grep tools over a directory | root_path, use_ripgrep=True, max_file_size_mb=10 |
Provider packages ship more middleware:
| Package | Middleware |
|---|---|
langchain_anthropic.middleware |
AnthropicPromptCachingMiddleware, ClaudeBashToolMiddleware, StateClaudeTextEditorMiddleware, StateClaudeMemoryMiddleware, FilesystemClaudeTextEditorMiddleware, FilesystemClaudeMemoryMiddleware, StateFileSearchMiddleware |
langchain_openai.middleware |
OpenAIModerationMiddleware (with OpenAIModerationError) |
langchain_fireworks.middleware |
FireworksPromptCachingMiddleware |
langchain_typesafe.experimental.middleware |
ModelRouterMiddleware, AutoModeMiddleware (alpha) |
Warning
Some docstring examples inside
langchain_anthropic.middlewareimport names such asStateTextEditorToolMiddlewarefromlangchain.agents.middleware. Those names do not exist — use theClaude*classes fromlangchain_anthropic.middlewarelisted above.
6.2 Writing your own middleware
With decorators — the quickest way:
from typing import Any
from langchain.agents import AgentState, create_agent
from langchain.agents.middleware import after_model, before_model, wrap_model_call, wrap_tool_call
from langchain.chat_models import init_chat_model
from langgraph.runtime import Runtime
@before_model
def log_size(state: AgentState, runtime: Runtime) -> None:
print(f"calling model with {len(state['messages'])} messages")
@before_model(can_jump_to=["end"])
def stop_on_bye(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
if state["messages"][-1].text.strip().lower() == "bye":
return {"jump_to": "end"}
return None
@wrap_model_call
def cheaper_model_for_short_chats(request, handler):
if len(request.messages) < 4:
request = request.override(model=init_chat_model("openai:gpt-5.4-mini"))
return handler(request)
@wrap_tool_call
def audit_tools(request, handler):
print("tool:", request.tool_call["name"], request.tool_call["args"])
return handler(request)
agent = create_agent("openai:gpt-5.5", tools=[get_weather],
middleware=[log_size, stop_on_bye, cheaper_model_for_short_chats, audit_tools])
With a class — when you need configuration, state or several hooks:
from typing import Any
from langchain.agents.middleware import AgentMiddleware, AgentState, hook_config
class MaxTurns(AgentMiddleware):
def __init__(self, limit: int) -> None:
super().__init__()
self.limit = limit
@hook_config(can_jump_to=["end"])
def after_model(self, state: AgentState, runtime) -> dict[str, Any] | None:
ai_turns = sum(1 for m in state["messages"] if m.type == "ai")
return {"jump_to": "end"} if ai_turns >= self.limit else None
| Hook | Runs | Return |
|---|---|---|
before_agent / abefore_agent |
Once, at the start of a run | state update dict, Command, or None |
before_model / abefore_model |
Before every model call | same |
wrap_model_call / awrap_model_call |
Around every model call; call handler(request) zero or more times |
ModelResponse, AIMessage, or ExtendedModelResponse |
after_model / aafter_model |
After every model call | state update dict, Command, or None |
wrap_tool_call / awrap_tool_call |
Around every tool execution | ToolMessage or Command |
after_agent / aafter_agent |
Once, at the end of a run | state update dict, Command, or None |
dynamic_prompt (decorator only) |
Before every model call | str or SystemMessage used as the system prompt |
Important
A class that defines only the sync
wrap_model_call/wrap_tool_callfails when the agent runs async (ainvoke/astream), and vice versa, withNotImplementedError: Asynchronous implementation of awrap_model_call is not available.... Decorators on a plain function handle both paths for you; for classes, implement both methods or run the agent in the matching mode.
Use request.override(model=..., system_message=..., messages=..., tools=..., tool_choice=..., response_format=..., model_settings=...) to change a model request. Assigning attributes directly (request.model = ...) still works but emits a DeprecationWarning.
Tip
To add custom state fields, give the middleware a
state_schemathat subclassesAgentState. Fields annotated withPrivateStateAttr/OmitFromInputare hidden from the agent's input and/or output schema. Each middleware instance must have a uniquename(the class name by default) — duplicates raiseAssertionError: Please remove duplicate middleware instances.
6.3 Running shell commands safely
from langchain.agents.middleware import DockerExecutionPolicy, RedactionRule, ShellToolMiddleware
shell = ShellToolMiddleware(
workspace_root="./workspace",
execution_policy=DockerExecutionPolicy(image="python:3.12-alpine3.19", network_enabled=False, read_only_rootfs=True),
redaction_rules=[RedactionRule(pii_type="email", strategy="redact")],
)
agent = create_agent("openai:gpt-5.5", middleware=[shell])
| Policy | Isolation | Use when |
|---|---|---|
HostExecutionPolicy (default) |
None; optional CPU/memory limits on POSIX | Trusted environments that are already sandboxed |
CodexSandboxExecutionPolicy |
Codex CLI sandbox (Seatbelt on macOS, Landlock/seccomp on Linux) | The codex CLI is installed |
DockerExecutionPolicy |
A container per run, --network none by default |
Untrusted commands or multi-tenant use |
Caution
With
HostExecutionPolicythe agent can modify anything the Python process user can reach. Redaction rules are applied after execution and do not prevent exfiltration.
6.4 Tracing and observability
export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY="lsv2_..."
export LANGSMITH_PROJECT="my-agent"
Every model call, tool call and middleware hook is then traced. To trim sensitive payloads from middleware spans:
from langchain.agents.middleware import TracePolicy, configure_trace_policy, omit_payload
configure_trace_policy(TracePolicy(process_inputs=omit_payload)) # process-wide default
For local debugging without LangSmith:
from langchain_core.globals import set_debug, set_verbose
set_debug(True) # prints every run start/end with inputs and outputs
set_verbose(True) # lighter console output
Token accounting across calls:
from langchain_core.callbacks import get_usage_metadata_callback
with get_usage_metadata_callback() as cb:
agent.invoke({"messages": [{"role": "user", "content": "Weather in Lima?"}]})
print(cb.usage_metadata)
{'gpt-5.5': {'input_tokens': 312, 'output_tokens': 41, 'total_tokens': 353}}
6.5 Rate limiting and caching
from langchain_core.rate_limiters import InMemoryRateLimiter
from langchain_core.caches import InMemoryCache
from langchain_core.globals import set_llm_cache
limiter = InMemoryRateLimiter(requests_per_second=2, check_every_n_seconds=0.1, max_bucket_size=5)
model = init_chat_model("openai:gpt-5.5", rate_limiter=limiter)
set_llm_cache(InMemoryCache(maxsize=1000)) # identical prompts are answered from memory
Note
InMemoryRateLimiterlimits requests per process only (not tokens), and its bucket starts empty, so the very first call waits briefly.InMemoryCacheevicts the oldest inserted entry (not least-recently-used) and has no TTL. Cache hits skip the rate limiter, and caching does not apply to streaming calls.
6.6 Streaming events from any runnable
async for event in chain.astream_events({"language": "German", "text": "Thanks"}, version="v2"):
if event["event"] == "on_chat_model_stream":
print(event["data"]["chunk"].text, end="")
v2 event names follow on_{chat_model|llm|chain|tool|retriever|prompt}_{start|stream|end}, plus on_tool_error and on_custom_event. There are no on_chain_error or on_llm_error events — errors surface as raised exceptions. version="v1" and astream_log are deprecated (removal in 2.0.0); version="v3" is a beta protocol supported only on chat models and compiled graphs.
6.7 Standard content blocks
Set output_version="v1" (or LC_OUTPUT_VERSION=v1) to store provider-independent content blocks in message.content. Regardless of that setting, message.content_blocks always gives the standardized view:
msg = init_chat_model("anthropic:claude-sonnet-4-5-20250929", output_version="v1").invoke("Think, then answer: 17*3?")
for block in msg.content_blocks:
print(block["type"])
reasoning
text
Block types: text, reasoning, image, audio, video, file, text-plain, tool_call, tool_call_chunk, invalid_tool_call, server_tool_call, server_tool_call_chunk, server_tool_result, non_standard. Build input blocks with helpers such as create_image_block(url=...) from langchain_core.messages.content.
7. How It Works
7.1 The package layers
your application
│
▼
langchain (1.4.3) ── create_agent · middleware · init_chat_model · init_embeddings · langchain.mcp
│ │
│ └── builds a LangGraph StateGraph (langgraph 1.2.x)
▼
langchain-core (1.6.6) ── Runnable protocol · messages · tools · prompts · parsers · callbacks
▲
│ implement core interfaces
langchain-openai · langchain-anthropic · langchain-ollama · ... (provider packages)
langchain-core defines interfaces and ships no third-party integrations. Provider packages subclass those interfaces (BaseChatModel, Embeddings, VectorStore, …). langchain adds the agent runtime on top. Because every chat model has the same interface, swapping providers is a one-string change.
7.2 The agent loop
create_agent compiles a graph shaped like this:
START → [before_agent hooks] → [before_model hooks] → model → [after_model hooks] ─┐
(once) ▲ │
│ tool calls pending? yes → tools ─────────┤
└──────────────────────────────────────────── │
no → [after_agent hooks] → END
before_agenthooks run once.before_modelhooks run, then the model node calls the model with the system prompt, messages and bound tools, wrapped by everywrap_model_callmiddleware.after_modelhooks run (in reverse registration order).- If the last
AIMessagehas tool calls that have noToolMessageyet, each is sent to the tools node (in parallel), wrapped by everywrap_tool_callmiddleware. Then the loop returns to step 2. - The loop ends when the model answers without tool calls, when every executed tool had
return_direct=True, when a structured-output tool succeeded, or when middleware setsjump_to: "end". after_agenthooks run once.
Malformed tool calls (arguments that could not be parsed) are answered automatically with an error ToolMessage ("...arguments were malformed or truncated.") so the conversation stays valid for the provider.
7.3 How middleware composes
- Node hooks (
before_*,after_*) become graph nodes named<MiddlewareName>.<hook>. A hook can return a state update, or{"jump_to": "model" | "tools" | "end"}if it declaredcan_jump_to. - Wrap hooks nest like decorators:
middleware=[A, B, C]executes asA(B(C(model_call))). The outer layer can call the innerhandlerseveral times (retry), skip it (cache/short-circuit), or change the request and response. - Tools that middleware registers (via its
toolsattribute) are added to the agent automatically.
7.4 Messages and state
Agent state is a dict whose messages list uses LangGraph's add_messages reducer: returned messages are appended, and a message with an existing id replaces the old one. RemoveMessage(id=...) deletes a message. structured_response holds the parsed structured output, and middleware may add its own keys (such as todos).
7.5 Where model capabilities come from
Chat models carry an optional profile — a dict of capabilities such as max_input_tokens, tool_calling and structured_output, generated from models.dev data by the langchain-profiles CLI and shipped inside provider packages (data/_profiles.py). create_agent uses it to choose a structured-output strategy, and SummarizationMiddleware needs it for ("fraction", ...) thresholds. When a model has no profile, pass profile={"max_input_tokens": ...} to the model constructor.
8. Troubleshooting
Entries quote the exact message raised by the source (placeholders such as {provider} are filled in at runtime; long messages are shortened with …). They are ordered: installation and model setup → agents → tools → structured output → middleware → memory and prompts → retrieval → MCP → provider-specific. Errors that link to https://docs.langchain.com/oss/python/langchain/errors/<CODE> carry one of the codes INVALID_PROMPT_INPUT, MESSAGE_COERCION_FAILURE or OUTPUT_PARSING_FAILURE.
Error: Initializing {class_name} requires the {pkg} package. Please install it with pip install {pkg}``
Cause: init_chat_model (or create_agent with a model string) resolved a provider whose integration package is not installed.
Solution: Install the package named in the message, e.g. pip install langchain-anthropic, or pip install "langchain[anthropic]".
Prevention: Pin provider packages in your requirements next to langchain.
Error: Unable to infer model provider for model={model!r}. Please specify 'model_provider' directly.
Cause: The model string has no provider: prefix and its name does not start with a recognized prefix (for example o4-mini, llama3, qwen).
Solution: Use the prefixed form ("openai:o4-mini", "ollama:llama3.1") or pass model_provider="...".
Prevention: Always use provider:model strings; inference is best-effort.
Error: Unsupported provider={provider!r}. Supported model providers are: …
Cause: The model_provider (or prefix) is not in init_chat_model's registry. Hyphens are normalized to underscores, so google-genai works but gemini does not.
Solution: Use a supported key from Section 10.1, or construct the provider's chat model class directly and pass the object.
Prevention: Copy provider keys from the reference table.
Error: `model` must be a string (e.g., 'openai:gpt-5.5'), got {type}. If you've already constructed a chat model object, use it directly instead of passing it to init_chat_model().
Cause: A model object was passed to init_chat_model.
Solution: Use the object directly — create_agent(model=my_chat_model).
Prevention: Only pass strings to init_chat_model.
Error: Inferred model_provider='google_vertexai' from {model_name!r}. This default will change to 'google_genai' in the next major release.
Cause: A bare gemini-... name was inferred as Vertex AI (a DeprecationWarning).
Solution: Use google_vertexai:gemini-... for Vertex AI or google_genai:gemini-... for the Gemini API / AI Studio.
Prevention: Always prefix Gemini model names.
Error: config_prefix={config_prefix!r} has been set but no fields are configurable.
Cause: init_chat_model(..., config_prefix="x") was given a model but no configurable_fields.
Solution: Add configurable_fields=("model", "temperature", ...) or remove config_prefix.
Prevention: Use config_prefix only for configurable models.
Error: {name} is not a BaseChatModel attribute and is not implemented on the default model.
Cause: An attribute was accessed on a configurable model (from init_chat_model with configurable_fields) that the underlying default model does not have.
Solution: Access provider attributes on a concrete model, or call only Runnable methods and bind_tools / with_structured_output on the configurable wrapper.
Prevention: Treat configurable models as Runnables.
Error: Provider '{provider}' is not supported. Supported providers and their required packages: …
Cause: init_embeddings was given a provider outside its 10 supported keys.
Solution: Use one of openai, azure_openai, azure_ai, bedrock, cohere, google_genai, google_vertexai, huggingface, mistralai, ollama, or instantiate the embeddings class directly.
Prevention: Check Section 10.4.
Error: Invalid model format '{model_name}'. Model name must be in format 'provider:model-name'
Cause: init_embeddings("text-embedding-3-small") — unlike chat models, embeddings never infer the provider.
Solution: init_embeddings("openai:text-embedding-3-small") or init_embeddings("text-embedding-3-small", provider="openai").
Prevention: Always include the provider for embeddings.
Error: Could not import {pkg} python package. Please install it with pip install {pkg}``
Cause: The embeddings provider's package is missing.
Solution: Install it (e.g. pip install langchain-ollama).
Prevention: Keep provider packages in your lockfile.
Error: Model name cannot be empty
Cause: An empty or whitespace-only model string was given to init_embeddings.
Solution: Provide a real model name.
Prevention: Validate config values before calling.
Error: Tracing using LangChainTracerV1 is no longer supported. Please set the LANGCHAIN_TRACING_V2 environment variable to enable tracing instead.
Cause: The legacy LANGCHAIN_TRACING or LANGCHAIN_HANDLER variable is set.
Solution: Unset them and set LANGSMITH_TRACING=true with LANGSMITH_API_KEY.
Prevention: Remove old tracing variables from .env files.
Error: Please remove duplicate middleware instances.
Cause: Two middleware in create_agent(middleware=[...]) share a name. The name defaults to the class name, so two instances of the same class collide — for example two ModelRetryMiddleware(...) with different settings.
Solution: Override the name property in a subclass, or combine the configuration into one instance. Several PIIMiddleware instances are allowed as long as their PII types differ, because the name is PIIMiddleware[<pii_type>].
Prevention: Give custom middleware unique names.
Error: Middleware added tools that the agent doesn't know how to execute. Unknown tools: [...] Registered tools: [...]
Cause: A wrap_model_call middleware added tools to request.tools that were never passed to create_agent.
Solution: Register the tools up front (create_agent(tools=[...]) or the middleware's tools attribute), or implement wrap_tool_call to execute dynamic tools yourself.
Prevention: Middleware should filter the tool list, not invent new tools.
Error: ToolStrategy specifies tool '{name}' which wasn't declared in the original response format when creating the agent.
Cause: Middleware replaced response_format with a ToolStrategy containing a schema that was not in the original.
Solution: Declare every possible schema in the original response_format (e.g. a union); middleware may only narrow it.
Prevention: Treat response_format overrides as subsets.
Error: Command goto is not yet supported in wrap_model_call middleware. Use the jump_to state field with before_model/after_model hooks instead.
Cause: A wrap_model_call returned ExtendedModelResponse(command=Command(goto=...)).
Solution: Use a before_model/after_model hook with can_jump_to and return {"jump_to": ...}.
Prevention: Only use Command(update=...) inside wrap_model_call. (Command.resume and Command.graph are likewise rejected.)
Error: Synchronous implementation of wrap_model_call is not available. You are likely encountering this error because you defined only the async version (awrap_model_call) and invoked your agent in a synchronous context …
Cause: Your middleware class implements only awrap_model_call, but you called agent.invoke()/stream().
Solution: Implement the sync method, use the @wrap_model_call decorator on a sync function, or call ainvoke()/astream().
Prevention: Implement both sync and async hooks in reusable middleware.
Error: Asynchronous implementation of awrap_model_call is not available. …
Cause: Only the sync wrap_model_call exists, and the agent ran with ainvoke()/astream() (MCP tools force the async path).
Solution: Implement awrap_model_call, or use the decorator form.
Prevention: Same as above.
Error: Synchronous implementation of wrap_tool_call is not available. …
Cause: Only awrap_tool_call exists and the agent ran synchronously.
Solution: Implement wrap_tool_call or run async.
Prevention: Implement both.
Error: Asynchronous implementation of awrap_tool_call is not available. …
Cause: Only wrap_tool_call exists and the agent ran asynchronously.
Solution: Implement awrap_tool_call or run sync.
Prevention: Implement both.
Error: Direct attribute assignment to ModelRequest.{name} is deprecated. Use request.override({name}=...) instead …
Cause: Middleware mutated a ModelRequest (request.model = ...).
Solution: request = request.override(model=...) and pass the new request to handler.
Prevention: Treat requests as immutable.
Error: Cannot specify both system_prompt and system_message
Cause: Both were passed to ModelRequest(...) or request.override(...).
Solution: Use only system_message=SystemMessage(...) (system_prompt is the deprecated form).
Prevention: Prefer system_message everywhere.
Error: Recursion limit of 9999 reached without hitting a stop condition. (LangGraph GraphRecursionError)
Cause: The agent looped without ending — for example a tool that keeps failing, or middleware that keeps jumping back to the model. GitHub issue #40767 reports this with PIIMiddleware(apply_to_output=True) blanking tool-call parameters.
Solution: Inspect the trace; add ModelCallLimitMiddleware(run_limit=...); if using PII output redaction on tool arguments, restrict it or use apply_to_input only.
Prevention: Always cap loops in production agents.
Error: Tool call {name} with id {id} could not be executed - arguments were malformed or truncated.
Cause: Not an exception — an automatic error ToolMessage inserted when the model produced an invalid tool call (often truncated JSON because max_tokens was too low).
Solution: Raise the model's max_tokens; the model normally retries.
Prevention: Give tool-heavy agents generous output limits.
Error: Tool definition for {name} must include valid type annotations for argument 'args_schema' to behave as expected.
Cause: A BaseTool subclass annotated args_schema: BaseModel instead of args_schema: type[BaseModel].
Solution: Annotate as args_schema: type[BaseModel] = MyArgs.
Prevention: Copy the annotation from existing tools.
Error: args_schema must be a subclass of pydantic BaseModel or a JSON schema dict. Got: …
Cause: An invalid object was passed as args_schema.
Solution: Pass a Pydantic model class or a JSON-schema dict.
Prevention: Validate tool definitions in unit tests.
Error: Arg {docstring_arg} in docstring not found in function signature.
Cause: @tool(parse_docstring=True) found an argument in the Args: section that the function does not have.
Solution: Fix the docstring or the signature, or pass error_on_invalid_docstring=False.
Prevention: Keep docstrings in sync with signatures.
Error: When tool includes an InjectedToolCallId argument, tool must always be invoked with a full model ToolCall of the form: {'args': {...}, 'name': '...', 'type': 'tool_call', 'tool_call_id': '...'}
Cause: A tool with an InjectedToolCallId parameter was called directly with plain arguments.
Solution: Invoke it with a full tool-call dict: tool.invoke({"args": {...}, "name": "t", "type": "tool_call", "id": "call_1"}).
Prevention: Test such tools through the agent or with tool-call dicts.
Error: String tool inputs are not allowed when using tools with JSON schema args_schema.
Cause: A tool defined with a JSON-schema dict args_schema was invoked with a string.
Solution: Invoke with a dict of arguments.
Prevention: Use dict inputs for structured tools.
Error: Too many arguments to single-input tool {name}. Consider using StructuredTool instead.
Cause: A single-input Tool received several arguments.
Solution: Use @tool on a typed function or StructuredTool.from_function.
Prevention: Avoid the legacy single-string Tool for multi-argument functions.
Error: Function and/or coroutine must be provided
Cause: StructuredTool.from_function() / Tool.from_function() was called without func or coroutine.
Solution: Pass at least one of them.
Prevention: Prefer the @tool decorator.
Error: The first argument must be a string or a callable with a __name__ for tool decorator.
Cause: @tool(...) was given an unexpected first argument.
Solution: Use @tool, @tool("name") or @tool(description="...").
Prevention: Pass options as keywords.
Error: Tool input must be str or dict. If dict, dict arguments must be typed. Either annotate types (e.g., with TypedDict) or pass arg_types into .as_tool to specify.
Cause: runnable.as_tool() could not infer an argument schema from an untyped runnable.
Solution: Pass args_schema= or arg_types={"x": int} to as_tool.
Prevention: Type your runnable inputs.
Error: Failed to generate JSON schema for '{model_name}': … Tool argument schemas must be JSON-serializable.
Cause: A tool argument uses a custom Python class that Pydantic cannot express as JSON Schema. Solution: Replace it with a Pydantic model, basic types, or a string representation. Prevention: Keep tool arguments JSON-friendly.
Error: Failed to parse structured output for tool '{tool_name}': {source}. (StructuredOutputValidationError)
Cause: The model's structured-output tool call did not validate against your schema, and error handling was off (or it came from ProviderStrategy).
Solution: Use ToolStrategy(schema, handle_errors=True) so the model gets the error and retries; loosen the schema; use a stronger model.
Prevention: Keep schemas simple and well described with Field(description=...).
Error: Model incorrectly returned multiple structured responses ({names}) when only one is expected. (MultipleStructuredOutputsError)
Cause: With a union ToolStrategy, the model called more than one schema tool in one turn.
Solution: Keep handle_errors=True (default) so the agent asks the model to correct itself.
Prevention: Describe in the system prompt when each schema applies.
Error: Native structured output expected valid JSON for {schema_name}, but parsing failed: {e}.
Cause: With ProviderStrategy, the model's text was not valid JSON (often a truncated response).
Solution: Increase max_tokens; switch to ToolStrategy; confirm the model supports native structured output.
Prevention: Let the default auto strategy choose.
Error: Unsupported schema type: {type}. Supported types: Pydantic models, dataclasses, TypedDicts, and JSON schema dicts.
Cause: response_format was given something else (e.g. a plain class or a str).
Solution: Convert the schema to one of the four supported kinds.
Prevention: Use Pydantic models for structured output.
Error: with_structured_output is not implemented for this model.
Cause: The chat model class does not implement bind_tools (for example the fake test models, or a minimal custom model).
Solution: Use a provider model that supports tool calling, or implement bind_tools.
Prevention: Check model.profile.get("tool_calling").
Error: Received unsupported arguments {kwargs}
Cause: Extra keyword arguments were passed to the base with_structured_output of a model that does not override it (only method and strict are accepted — and ignored — there).
Solution: Remove the extra arguments or use a provider model whose with_structured_output supports them.
Prevention: Check the provider's signature.
Error: schema must be specified when method is not 'json_mode'. Received None.
Cause: with_structured_output(None, method=...) on OpenAI/Ollama without json_mode.
Solution: Pass a schema, or use method="json_mode".
Prevention: Always pass a schema.
Error: This model does not support OpenAI's structured output feature, which is the default method for with_structured_output as of langchain-openai==0.3.
Cause: An OpenAI-compatible model rejected response_format of type json_schema (a UserWarning accompanying the API error).
Solution: Use with_structured_output(schema, method="function_calling").
Prevention: Choose the method explicitly for OpenAI-compatible servers.
Error: Invalid schema for OpenAI's structured output feature …
Cause: OpenAI rejected the JSON schema (strict mode forbids some constructs).
Solution: Use method="function_calling" or adjust the schema (no unsupported keywords, all fields required in strict mode).
Prevention: Test schemas against the provider before shipping.
Error: Invalid interrupt_onconfig for tool '{tool_name}':allowed_decisions must be a non-empty list of decision types …
Cause: A HumanInTheLoopMiddleware entry was a dict without a non-empty allowed_decisions (often a typo such as allowed_decision).
Solution: Use {"allowed_decisions": ["approve", "reject"]}, or True for all decisions.
Prevention: Build entries with InterruptOnConfig(...) for type checking.
Error: Number of human decisions ({n}) does not match number of hanging tool calls ({m}).
Cause: The Command(resume={"decisions": [...]}) list length differs from len(interrupt.value["action_requests"]).
Solution: Send exactly one decision per action request, in order.
Prevention: Build decisions by iterating over action_requests.
Error: Unexpected human decision: {decision}. Decision type '{type}' is not allowed for tool '{tool}'. Expected one of {allowed_decisions} based on the tool's configuration.
Cause: The reviewer sent a decision type (e.g. edit) not allowed for that tool.
Solution: Offer reviewers only the review_configs[i]["allowed_decisions"] options.
Prevention: Drive your approval UI from review_configs.
Error: Reviewer edited tool call {id!r} to {name!r}, which is not an available tool. Available tools: {names}.
Cause: An edit decision named a tool the agent does not have.
Solution: Restrict edits to registered tool names.
Prevention: Validate edited actions in your UI.
Error: At least one limit must be specified (thread_limit or run_limit)
Cause: ModelCallLimitMiddleware() or ToolCallLimitMiddleware() with no limits.
Solution: Pass run_limit= and/or thread_limit=.
Prevention: —
Error: Invalid exit_behavior: {exit_behavior}. Must be 'end' or 'error'
Cause: ModelCallLimitMiddleware only supports "end" and "error" (not "continue").
Solution: Use one of those two values.
Prevention: Note that ToolCallLimitMiddleware additionally supports "continue".
Error: run_limit ({run_limit}) cannot exceed thread_limit ({thread_limit}).
Cause: ToolCallLimitMiddleware was configured with a run limit above the thread limit.
Solution: Make run_limit <= thread_limit.
Prevention: —
Error: Model call limits exceeded: thread limit ({n}/{limit}) (ModelCallLimitExceededError)
Cause: ModelCallLimitMiddleware(exit_behavior="error") hit its limit.
Solution: Catch the exception, or use exit_behavior="end" to finish gracefully with an explanatory AI message.
Prevention: Set limits from observed usage. Thread limits persist across runs of the same thread_id.
Error: '{tool_name}' tool call limit reached: run limit exceeded ({n}/{limit} calls). (ToolCallLimitExceededError)
Cause: ToolCallLimitMiddleware(exit_behavior="error") hit its limit.
Solution: Catch it or use "continue" (blocks the tool, lets the model continue) / "end".
Prevention: —
Error: max_retries must be >= 0
Cause: Invalid retry configuration (siblings: initial_delay must be >= 0, max_delay must be >= 0, backoff_factor must be >= 0).
Solution: Use non-negative values.
Prevention: —
Error: on_failure='raise' is deprecated and will be removed in a future version. Use on_failure='error' instead.
Cause: Old ToolRetryMiddleware option (similarly 'return_message' → 'continue').
Solution: Use the new names.
Prevention: —
Error: ToolErrorMiddleware requires on_errorand/oraon_error.
Cause: ToolErrorMiddleware() was created with no handler.
Solution: Pass on_error=lambda exc, request: ....
Prevention: —
Error: ToolErrorMiddleware has no sync on_error; run async (ainvoke/astream) or provide on_error.
Cause: Only aon_error was given but the agent runs synchronously.
Solution: Add on_error or run async.
Prevention: Provide both handlers for dual-mode agents.
Error: Model profile information is required to use fractional token limits, and is unavailable for the specified model.
Cause: SummarizationMiddleware uses ("fraction", x) but the model has no max_input_tokens profile.
Solution: Use ("tokens", N), or construct the model with profile={"max_input_tokens": 128000}.
Prevention: Prefer absolute token triggers for self-hosted models.
Error: trigger clause must specify at least one of 'tokens', 'messages', 'fraction'
Cause: An empty dict was given as a trigger clause. Related: Unsupported trigger metric: {k!r}, {k} trigger values must be integers, got {v!r}, Fractional {name} values must be between 0 and 1, got {value}., {name} thresholds must be greater than 0, got {value}.
Solution: Use {"tokens": 4000}, ("messages", 50) or ("fraction", 0.8).
Prevention: Keep trigger values typed correctly (ints for tokens/messages, 0–1 floats for fraction).
Error: max_tokens_before_summary is deprecated. Use trigger=('tokens', value) instead.
Cause: Old SummarizationMiddleware keyword (similarly messages_to_keep → keep=('messages', value)).
Solution: Use trigger= and keep=.
Prevention: —
Error: Detected {count} instance(s) of {pii_type} in text content (PIIDetectionError)
Cause: PIIMiddleware(..., strategy="block") found PII.
Solution: Catch PIIDetectionError and ask the user to resubmit, or switch to redact/mask.
Prevention: Use block only where refusing is acceptable.
Error: Unknown PII type: {pii_type}. Must be one of ['email', 'credit_card', 'ip', 'mac_address', 'url'] or provide a custom detector.
Cause: A custom PII type was named without a detector.
Solution: Add detector=r"regex" or a function.
Prevention: —
Error: LLMToolEmulator's default model ('anthropic:claude-sonnet-4-5-20250929') is deprecated and will be removed in a future release. Pass model explicitly instead.
Cause: LLMToolEmulator() without model=.
Solution: Pass model="openai:gpt-5.4-mini" or any model.
Prevention: Always pass models explicitly to middleware.
Error: LLMToolEmulator's default model requires langchain-anthropic to be installed.
Cause: The implicit default model needs the Anthropic package.
Solution: Pass model= or install langchain-anthropic.
Prevention: Same as above.
Error: Tools in always_include not found in request: {missing}. Available tools: {names}
Cause: LLMToolSelectorMiddleware(always_include=[...]) named a tool the agent does not have.
Solution: Fix the names.
Prevention: Derive the list from your tools: [t.name for t in tools].
Error: LLMToolSelectorMiddleware: selection model returned a malformed response after {n} retries (expected a dict with a 'tools' list): …
Cause: The selector model did not return structured output.
Solution: Use a model that supports structured output, raise max_retries, or set on_parsing_failure="all" / "none" / a fallback list.
Prevention: Use a capable selector model.
Error: Model selected invalid tools: {names}
Cause: The selector model returned tool names that do not exist. Solution: Improve tool descriptions or use a stronger selector model. Prevention: Keep tool names short and distinct.
Error: ProviderToolSearchMiddleware could not determine the provider for model {name!r}; server-side tool search supports: anthropic, openai
Cause: Tools were deferred but the model is not an Anthropic or OpenAI model (GitHub issue #40876 reported this for ChatAnthropicBedrock).
Solution: Use a supported provider, or remove ProviderToolSearchMiddleware.
Prevention: Use LLMToolSelectorMiddleware for provider-independent tool filtering.
Error: ProviderToolSearchMiddleware: searchable_tools references tool(s) not bound to the model: {names}
Cause: searchable_tools lists tools not passed to the agent.
Solution: Pass those tools to create_agent.
Prevention: —
Error: Path traversal not allowed
Cause: A file-search (or Claude text-editor) tool call used .. or ~ in a path.
Solution: None needed — this is a safety check; the model gets the error.
Prevention: Tell the model paths are relative to the workspace root.
Error: Shell tool requires either 'command' or 'restart'.
Cause: The model called the shell tool with neither argument (or both: Specify only one of 'command' or 'restart'.).
Solution: Usually self-corrects; improve tool_description if frequent.
Prevention: —
Error: Startup command '{command}' failed with exit code {code}
Cause: One of ShellToolMiddleware(startup_commands=...) failed.
Solution: Run the command manually in the same environment and fix it.
Prevention: Keep startup commands idempotent.
Error: HostExecutionPolicy cpu/memory limits require the Python 'resource' module. Either remove the limits or run on a POSIX platform.
Cause: cpu_time_seconds or memory_bytes set on Windows.
Solution: Remove the limits, or use DockerExecutionPolicy.
Prevention: Use Docker isolation on Windows hosts.
Error: Docker execution policy requires the 'docker' CLI to be installed and available on PATH.
Cause: DockerExecutionPolicy could not find the docker binary (similarly Codex sandbox policy requires the 'codex' CLI…).
Solution: Install Docker or pass binary="/path/to/docker".
Prevention: —
Error: DockerExecutionPolicy does not support cpu_time_seconds; configure CPU limits using Docker run options such as '--cpus'.
Cause: cpu_time_seconds given to the Docker policy.
Solution: Use cpus="1.5" instead.
Prevention: —
Error: Input to ChatPromptTemplate is missing variables {missing}. Expected: [...] Received: [...]
Cause: A prompt was invoked without all template variables. A common trigger is literal braces in the template (e.g. JSON examples).
Solution: Supply the variables, or escape literal braces as {{ and }}.
Prevention: Test prompts with prompt.input_variables.
Error: Cannot have an input variable named 'stop', as it is used internally, please rename.
Cause: stop is reserved.
Solution: Rename the variable.
Prevention: —
Error: Found overlapping input and partial variables: {names}
Cause: A variable was both partially filled and declared as input. Solution: Remove it from one of the two. Prevention: —
Error: Invalid format specifier in f-string template. Nested replacement fields are not allowed.
Cause: An f-string template contains {x:{y}}; attribute or index access ({a.b}, {a[0]}) is also rejected.
Solution: Pre-compute values into plain variables.
Prevention: Keep templates to simple {name} fields.
Error: Message dict must contain 'role' and 'content' keys, got {message}
Cause: A dict message lacked role or content.
Solution: Use {"role": "user", "content": "..."}.
Prevention: —
Error: Unexpected message type: '{type}'. Use one of 'human', 'user', 'ai', 'assistant', 'function', 'tool', 'system', or 'developer'.
Cause: An unknown role such as "chat" or "bot".
Solution: Use a supported role.
Prevention: Normalize roles when importing chat logs.
Error: Message as a sequence must be (role string, template)
Cause: A tuple message had the wrong length.
Solution: Use two-element tuples: ("human", "hi").
Prevention: —
Error: ToolMessage content should be a string or a list of string/dicts.
Cause: A tool returned an object that could not be coerced to a string.
Solution: Return a string, a list of content blocks, or use response_format="content_and_artifact" to attach the object as an artifact.
Prevention: Serialize tool outputs explicitly.
Error: Using fallback GPT-2 tokenizer for token counting. Token counts may be inaccurate for non-GPT-2 models.
Cause: get_num_tokens fell back to the default GPT-2 tokenizer (requires transformers).
Solution: Use a provider model with its own token counting, or count_tokens_approximately.
Prevention: Use token_counter="approximate" in trim_messages.
Error: Could not import transformers python package. This is needed in order to calculate get_token_ids.
Cause: Default token counting needs transformers.
Solution: pip install transformers, or use a provider-specific counter / count_tokens_approximately.
Prevention: —
Error: Asked to cache, but no cache found at langchain.cache.
Cause: A model was created with cache=True but no global cache was set.
Solution: Call set_llm_cache(InMemoryCache()) first.
Prevention: —
Error: Invalid json output: {text} (OutputParserException)
Cause: JsonOutputParser received text that is not JSON.
Solution: Prefer with_structured_output; otherwise add format instructions via parser.get_format_instructions().
Prevention: Use structured output features over text parsing.
Error: defusedxml is not installed. Please install it to use the defusedxml parser.
Cause: XMLOutputParser uses defusedxml by default.
Solution: pip install defusedxml.
Prevention: Do not switch to parser="xml" unless your stdlib XML is known-safe.
Error: If 'exception_key' is specified then input must be a dictionary.
Cause: with_fallbacks(..., exception_key="error") used with a non-dict input.
Solution: Pass dict inputs, or drop exception_key.
Prevention: Note the key is written into your input dict in place.
Error: The input to RunnablePassthrough.assign() must be a dict.
Cause: .assign() / .pick() received a non-dict.
Solution: Feed a dict (e.g. start the chain with a dict of inputs).
Prevention: —
Error: Expected a Runnable, callable or dict.Instead got an unsupported type: {type}
Cause: Something that is not a Runnable, function or dict was put in a | chain (for example a string or a non-dict mapping).
Solution: Wrap it: RunnableLambda(fn) or a real dict.
Prevention: —
Error: Recursion limit reached when invoking {runnable} with input {input}.
Cause: A RunnableLambda kept returning runnables past recursion_limit (default 25).
Solution: Fix the recursion, or raise config={"recursion_limit": N}.
Prevention: —
Error: astream_events(version='v3') is only supported on Runnable subclasses that implement the v3 streaming protocol (BaseChatModel, CompiledGraph).
Cause: v3 events requested on a chain.
Solution: Use version="v2".
Prevention: —
Error: search_type of {search_type} not allowed. Valid values are: ('similarity', 'similarity_score_threshold', 'mmr')
Cause: A typo in as_retriever(search_type=...).
Solution: Use one of the three values.
Prevention: —
Error: Relevance scores must be between 0 and 1, got {...}
Cause: A UserWarning from a vector store whose relevance function does not match its distance metric.
Solution: Pass relevance_score_fn or use a normalized metric.
Prevention: —
Error: Using SHA-1 for document hashing. SHA-1 is *not* collision-resistant …
Cause: index() uses the default key_encoder="sha1" (one-time UserWarning).
Solution: Pass key_encoder="blake2b" (or sha256/sha512) and rebuild the index once.
Prevention: Choose the encoder before your first indexing run.
Error: Unable to import from langchain_text_splitters. Please specify text_splitter or install langchain_text_splitters
Cause: loader.load_and_split() needs the text-splitters package.
Solution: pip install -U langchain-text-splitters, or split explicitly.
Prevention: Call splitter.split_documents(loader.load()) instead.
Error: chunk_size must be > 0, got {chunk_size}
Cause: Invalid splitter settings; chunk_overlap must also be ≥ 0 and ≤ chunk_size.
Solution: Fix the values.
Prevention: —
Error: Could not import tiktoken python package. … Please install it with pip install tiktoken.
Cause: TokenTextSplitter / from_tiktoken_encoder need tiktoken (text-splitters declares no extras).
Solution: pip install tiktoken.
Prevention: Install optional back ends explicitly (NLTK, spaCy, sentence-transformers, bs4, lxml).
Error: json_data must be a dict, got {type}. Top-level lists can be split by passing convert_lists=True.
Cause: RecursiveJsonSplitter.split_json received a list.
Solution: Pass convert_lists=True.
Prevention: —
Error: Please install FastMCP to use MCPAdapter—pip install fastmcp.
Cause: langchain.mcp imported without FastMCP.
Solution: pip install "langchain[mcp]".
Prevention: —
Error: MCP target {target!r} is not a valid URL. To run a local MCP server over stdio, ask for it explicitly with Path('server.py'), a fastmcp transport, or an MCPConfig …
Cause: A string that is not an http(s) URL was passed to MCPAdapter — including local script paths.
Solution: Wrap local scripts in pathlib.Path(...), or use an MCPConfig dict.
Prevention: This is deliberate: string targets can never launch subprocesses.
Error: MCP target {target!r} has scheme {scheme!r}, but a string target must be an http or https URL.
Cause: file:, ws: or a Windows path like C:\server.py was passed as a string.
Solution: Same as above.
Prevention: —
Error: Resuming the MCP tool {tool!r} needs an answer for every elicitation request, but these had none: {keys}.
Cause: The resume responses dict missed some request keys.
Solution: Answer every request["key"] from the interrupt payload with accept, decline or cancel.
Prevention: Iterate over interrupt.value["requests"].
Error: Elicitation answer for {key!r} has action {action!r}; expected 'accept', 'decline', or 'cancel'.
Cause: Invalid action in an elicitation answer.
Solution: Use one of the three actions.
Prevention: —
Error: The MCP tool {tool!r} asked for input that interrupt-based elicitation cannot answer: {kinds}. Only elicitation requests are supported.
Cause: The server asked for sampling or roots, which langchain.mcp refuses.
Solution: Use a pre-built fastmcp.Client with your own handlers, or a server that does not need them.
Prevention: —
Error: Converting MCP audio content to a LangChain content block is not yet supported.
Cause: An MCP tool returned audio. Solution: Have the server return a file/resource instead. Prevention: —
Error: Unknown MCP content type: {type}. This usually means the installed mcp is newer than the version this adapter supports.
Cause: Version skew between mcp/fastmcp and langchain.
Solution: Upgrade langchain, or pin fastmcp to the range in langchain[mcp] (>=4.0.1,<5.0.0).
Prevention: Upgrade them together.
Error: Anthropic authentication failed: no API key or authorization credentials were provided. Set the ANTHROPIC_API_KEY environment variable, pass api_key=... to ChatAnthropic, …
Cause: No Anthropic credentials.
Solution: export ANTHROPIC_API_KEY=... or pass api_key=.
Prevention: —
Error: Received multiple non-consecutive system messages.
Cause: Anthropic accepts system messages only at the start (or in supported mid-conversation positions).
Solution: Merge system instructions into one leading SystemMessage.
Prevention: Use system_prompt= on the agent rather than inserting system messages mid-conversation.
Error: A non-leading SystemMessage was moved to the top-level system field and now applies to the entire conversation.
Cause: A UserWarning from ChatAnthropic: your mid-conversation system message was hoisted.
Solution: Put instructions in the leading system prompt, or use a model that supports mid-conversation system messages.
Prevention: —
Error: Malformed url parameter. Must be either an image URL (https://example.com/image.jpg) or base64 encoded string (data:image/png;base64,...)
Cause: An image block for Anthropic had an invalid URL. GitHub issue #40905 notes base64 data URLs with line breaks were rejected.
Solution: Use create_image_block(base64=..., mime_type="image/png") with a single-line base64 string.
Prevention: Strip newlines from base64 data.
Error: Sync client is not available. This happens when an async callable was provided for the API key. Use async methods (ainvoke, astream) instead, …
Cause: ChatOpenAI(api_key=async_fn) was used synchronously.
Solution: Use ainvoke/astream, or provide a string or sync callable.
Prevention: —
Error: Cannot specify 'openai_proxy' if one of 'http_client'/'http_async_client' is already specified.
Cause: Both a proxy and a custom HTTP client were given.
Solution: Configure the proxy on your own httpx client, or drop the custom client.
Prevention: —
Error: Received response with null value for 'choices'. This can happen when using OpenAI-compatible APIs (e.g., vLLM) that return a response in an unexpected format.
Cause: An OpenAI-compatible server returned a non-standard response. Solution: Check the server's API compatibility; use a dedicated provider package if one exists. Prevention: —
Error: As of openai>=1.0.0, Azure endpoints should be specified via the azure_endpointparam notopenai_api_base(or aliasbase_url).
Cause: AzureChatOpenAI(base_url=...) without /openai in the URL, or combined with azure_deployment.
Solution: Use azure_endpoint="https://<resource>.openai.azure.com/" plus azure_deployment=.
Prevention: Set AZURE_OPENAI_ENDPOINT and OPENAI_API_VERSION.
Error: n must be 1 when streaming.
Cause: n > 1 combined with streaming=True (OpenAI, Azure, Groq, xAI, Fireworks, OpenRouter).
Solution: Use n=1 when streaming.
Prevention: —
Error: Model {model}not found in Ollama. Please pull the model (usingollama pull {model}) or specify a valid model name.
Cause: ChatOllama(..., validate_model_on_init=True) and the model is not pulled.
Solution: ollama pull <model>.
Prevention: —
Error: Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download
Cause: The Ollama server is not reachable at base_url (or the SDK default).
Solution: Start Ollama; pass base_url="http://host:11434" for remote servers (OLLAMA_HOST is not read by LangChain).
Prevention: —
Error: temperature must be in the range [0.0, 1.0]
Cause: ChatMistralAI restricts temperature and top_p to 0–1.
Solution: Use a value in range.
Prevention: —
Error: xAI API key is not set. Please set it in the xai_api_keyfield or in theXAI_API_KEY environment variable.
Cause: Missing xAI key. Similar provider messages: If using default api base, DEEPSEEK_API_KEY must be set., OPENROUTER_API_KEY must be set., You must specify an api key. … FIREWORKS_API_KEY.
Solution: Set the named environment variable or pass api_key=.
Prevention: —
Error: Perplexity API key not provided. Pass pplx_api_key(orapi_key) … or set the PPLX_API_KEYorPERPLEXITY_API_KEY environment variable.
Cause: Missing Perplexity key. Note ChatPerplexity itself reads only PPLX_API_KEY.
Solution: Set PPLX_API_KEY.
Prevention: —
Error: search_parameters (Live Search) is deprecated by xAI and is ignored.
Cause: ChatXAI(search_parameters=...).
Solution: ChatXAI(model="...").bind_tools([{"type": "web_search"}]).
Prevention: —
Error: Only one of 'persist_directory', 'host' and 'chroma_cloud_api_key' is allowed, but got …
Cause: Conflicting Chroma(...) connection options.
Solution: Choose local persistence, a server host, or Chroma Cloud (which also needs tenant and database).
Prevention: —
Error: The 'fastembed' package is not installed.
Cause: FastEmbedSparse needs pip install "langchain-qdrant[fastembed]" (Python < 3.13).
Solution: Install the extra.
Prevention: —
9. FAQ
Q: Which package should I import from — langchain, langchain_core or langchain_classic?
Use langchain for agents, middleware and model initialization; it re-exports the everyday types (langchain.messages, langchain.tools, langchain.chat_models, langchain.embeddings, langchain.rate_limiters). Use langchain_core for prompts, output parsers, runnables, documents, vector store interfaces and callbacks. Use langchain_classic only to keep old code running.
Q: Do I need to learn LangGraph?
No. create_agent builds and compiles the LangGraph graph for you. You will meet LangGraph concepts only for persistence (checkpointers, thread_id), interrupts (Command(resume=...)) and stream modes.
Q: Is AgentExecutor still supported?
It lives in langchain-classic and is superseded by create_agent. Classic memory classes are deprecated with create_agent as the named alternative.
Q: How do I swap OpenAI for Anthropic or a local model?
Change the model string: "openai:gpt-5.5" → "anthropic:claude-sonnet-4-5-20250929" or "ollama:llama3.1", and install the provider package. Tools, middleware and structured output keep working.
Q: Why doesn't my agent remember previous messages?
You need both a checkpointer= on create_agent and the same config={"configurable": {"thread_id": ...}} on every call.
Q: How do I stop an agent from looping forever?
Add ModelCallLimitMiddleware(run_limit=N) and/or ToolCallLimitMiddleware(run_limit=N). The graph's own recursion limit is set very high (9,999) by design.
Q: Can one agent use tools from several MCP servers?
Yes — pass an {"mcpServers": {...}} dict to one MCPAdapter; tool names are prefixed with each server's key.
Q: Does LangChain send my data anywhere besides the model provider?
Only if you enable tracing (LANGSMITH_TRACING=true), which sends run inputs and outputs to LangSmith, or configure the LangSmith gateway. Use TracePolicy(process_inputs=omit_payload) to drop payloads from middleware spans.
Q: What is the difference between context and state?
context is immutable per-run data you pass to invoke(..., context=...) and read via runtime.context. state (messages, structured_response, middleware fields) evolves during the run and is saved by the checkpointer.
Q: Why is max_tokens different on my Anthropic model than I expected?
ChatAnthropic fills max_tokens from the model's profile (max_output_tokens), falling back to 4096 when no profile exists. Set it explicitly for predictable behavior.
Q: How do I count tokens and cost across an agent run?
Wrap the call in with get_usage_metadata_callback() as cb: and read cb.usage_metadata, keyed by model name. Each AIMessage also carries usage_metadata.
Q: Can tools return images or files to the model?
Yes — return a list of content blocks (e.g. create_image_block(...)), provided the model supports image tool messages (see the profile's image_tool_message).
Q: How do I test agents without calling a real model?
Use GenericFakeChatModel(messages=iter([...])) or FakeListChatModel(responses=[...]) from langchain_core.language_models, and LLMToolEmulator to fake tool results. Note that fake models do not support with_structured_output.
Q: Is the openwiki/ folder in the repository official documentation?
It is an automatically generated code wiki. The repository's own AGENTS.md says to treat source code and tests as authoritative over it.
Q: Why am I getting deprecation warnings I did not ask for?
Importing langchain_core turns on display of LangChain's deprecation and beta warnings. Wrap code in langchain_core._api.suppress_langchain_deprecation_warning() or suppress_langchain_beta_warning() to silence them locally.
Q: Which Python versions are tested?
CI runs unit tests for langchain-core on 3.10 through 3.14 and for other packages on 3.10 and 3.14.
10. Reference
10.1 init_chat_model providers
model_provider |
Package | Class |
|---|---|---|
openai |
langchain-openai |
ChatOpenAI |
azure_openai |
langchain-openai |
AzureChatOpenAI |
azure_ai |
langchain-azure-ai |
AzureAIOpenAIApiChatModel |
anthropic |
langchain-anthropic |
ChatAnthropic |
anthropic_bedrock |
langchain-aws |
ChatAnthropicBedrock |
bedrock |
langchain-aws |
ChatBedrock |
bedrock_converse |
langchain-aws |
ChatBedrockConverse |
bedrock_mantle_anthropic |
langchain-aws[anthropic] |
ChatAnthropicMantle |
bedrock_mantle_openai |
langchain-aws[openai] |
ChatOpenAIMantle |
google_vertexai |
langchain-google-vertexai |
ChatVertexAI |
google_genai |
langchain-google-genai |
ChatGoogleGenerativeAI |
google_anthropic_vertex |
langchain-google-vertexai |
ChatAnthropicVertex |
cohere |
langchain-cohere |
ChatCohere |
mistralai |
langchain-mistralai |
ChatMistralAI |
groq |
langchain-groq |
ChatGroq |
fireworks |
langchain-fireworks |
ChatFireworks |
together |
langchain-together |
ChatTogether |
ollama |
langchain-ollama (falls back to langchain-community) |
ChatOllama |
huggingface |
langchain-huggingface |
ChatHuggingFace.from_model_id(model_id=...) |
ibm |
langchain-ibm |
ChatWatsonx(model_id=...) |
nvidia |
langchain-nvidia-ai-endpoints |
ChatNVIDIA |
deepseek |
langchain-deepseek |
ChatDeepSeek |
xai |
langchain-xai |
ChatXAI |
perplexity |
langchain-perplexity |
ChatPerplexity |
openrouter |
langchain-openrouter |
ChatOpenRouter |
upstage |
langchain-upstage |
ChatUpstage |
baseten |
langchain-baseten |
ChatBaseten |
litellm |
langchain-litellm |
ChatLiteLLM |
meta |
langchain-meta |
ChatMetaModel |
langsmith |
langchain-openai |
ChatOpenAI via the LangSmith gateway (Responses API) |
10.2 Provider inference from bare model names
| Name starts with (case-insensitive) | Inferred provider |
|---|---|
gpt-, o1, o3, chatgpt, text-davinci |
openai |
claude |
anthropic |
command |
cohere |
accounts/fireworks |
fireworks |
gemini |
google_vertexai (with a DeprecationWarning) |
amazon., anthropic., meta. |
bedrock |
mistral, mixtral |
mistralai |
deepseek |
deepseek |
grok |
xai |
sonar |
perplexity |
solar |
upstage |
10.3 create_agent parameters
| Parameter | Type | Default |
|---|---|---|
model |
str | BaseChatModel |
required |
tools |
Sequence[BaseTool | Callable | dict] | None |
None |
system_prompt |
str | SystemMessage | None |
None |
middleware |
Sequence[AgentMiddleware] |
() |
response_format |
ToolStrategy | ProviderStrategy | type | dict | None |
None |
state_schema |
type[AgentState] | None |
None |
context_schema |
type | None |
None |
checkpointer |
Checkpointer | None |
None |
store |
BaseStore | None |
None |
interrupt_before, interrupt_after |
list[str] | None |
None |
debug |
bool |
False |
name |
str | None |
None |
cache |
BaseCache | None (LangGraph cache) |
None |
transformers |
Sequence[TransformerFactory] | None |
None |
Graph node names: model, tools, and <MiddlewareName>.before_agent / .before_model / .after_model / .after_agent — use these in interrupt_before / interrupt_after.
10.4 init_embeddings providers
| Provider | Package | Class |
|---|---|---|
openai |
langchain-openai |
OpenAIEmbeddings |
azure_openai |
langchain-openai |
AzureOpenAIEmbeddings |
azure_ai |
langchain-azure-ai |
AzureAIOpenAIApiEmbeddingsModel |
bedrock |
langchain-aws |
BedrockEmbeddings(model_id=...) |
cohere |
langchain-cohere |
CohereEmbeddings |
google_genai |
langchain-google-genai |
GoogleGenerativeAIEmbeddings |
google_vertexai |
langchain-google-vertexai |
VertexAIEmbeddings |
huggingface |
langchain-huggingface |
HuggingFaceEmbeddings(model_name=...) |
mistralai |
langchain-mistralai |
MistralAIEmbeddings |
ollama |
langchain-ollama |
OllamaEmbeddings |
10.5 Public import map
| Import path | Names |
|---|---|
langchain.agents |
create_agent, AgentState |
langchain.agents.middleware |
all built-in middleware, AgentMiddleware, ModelRequest, ModelResponse, ExtendedModelResponse, ToolCallRequest, InterruptOnConfig, ClearToolUsesEdit, execution policies, RedactionRule, PIIMatch, PIIDetectionError, TriggerClause, TracePolicy, configure_trace_policy, omit_payload, Runtime, decorators before_agent, before_model, after_model, after_agent, wrap_model_call, wrap_tool_call, dynamic_prompt, hook_config |
langchain.agents.structured_output |
ToolStrategy, ProviderStrategy, AutoStrategy, StructuredOutputError, StructuredOutputValidationError, MultipleStructuredOutputsError |
langchain.chat_models |
init_chat_model, BaseChatModel |
langchain.embeddings |
init_embeddings, Embeddings |
langchain.messages |
message classes, content-block types, trim_messages |
langchain.tools |
tool, BaseTool, ToolException, InjectedToolArg, InjectedToolCallId, InjectedState, InjectedStore, ToolRuntime |
langchain.rate_limiters |
BaseRateLimiter, InMemoryRateLimiter |
langchain.mcp (beta) |
MCPAdapter, MCPToolArtifact, as_langchain_tool |
10.6 CLI Reference
LangChain has no end-user command-line interface. The only console script in the monorepo is langchain-profiles, a maintainer tool from langchain-model-profiles that regenerates model capability data for provider packages.
| Command | Flag | Required | Default | Description |
|---|---|---|---|---|
langchain-profiles refresh |
--provider |
yes | — | Provider ID from models.dev (e.g. anthropic, openai, google) |
--data-dir |
yes | — | Directory containing profile_augmentations.toml; _profiles.py is written there |
|
langchain-profiles summarize |
--providers |
yes | — | JSON array of {"provider": ..., "data_dir": ...} objects (paths relative to the repo root) |
--base-ref |
no | HEAD |
Git ref to compare the working tree against | |
--repo-root |
no | current directory | Repository root |
cd libs/model-profiles
uv run langchain-profiles refresh --provider openai --data-dir ../partners/openai/langchain_openai/data
Provider: openai
Data directory: ../partners/openai/langchain_openai/data
Downloading data from https://models.dev/api.json...
Downloaded <N> providers with <M> models
Extracted <K> models for openai
Loading augmentations...
Writing to ../partners/openai/langchain_openai/data/_profiles.py...
Note
When
--data-diris outside the current directory the CLI asksContinue? (y/N):; pipeecho y |to confirm non-interactively. Therefreshcommand fetcheshttps://models.dev/api.jsonwith a 30-second timeout.
10.7 Useful commands
| Task | Command |
|---|---|
| Install framework + OpenAI | pip install -U "langchain[openai]" |
| Install MCP support | pip install -U "langchain[mcp]" |
| Show versions for a bug report | python -m langchain_core.sys_info |
| Enable tracing | export LANGSMITH_TRACING=true LANGSMITH_API_KEY=... |
| Default to standard content blocks | export LC_OUTPUT_VERSION=v1 |