langchain-ai/langchain
Patterns and conventions
Coding norms enforced across langchain-ai/langchain. Most of these come from CLAUDE.md / AGENTS.md; the rest are observable in the source.
Stable public interfaces
LangChain treats every symbol exported from a package's __init__.py as a public API. Breaking changes are rare and require a deprecation cycle.
Rules of thumb before changing a public function or class:
- Check whether the symbol is re-exported from
libs/<package>/<package>/__init__.py. If it is, treat the signature as load-bearing. - New parameters should be keyword-only:
def f(..., *, new_param: T = default). - Don't change defaults of arguments like
model=in shipped code unless you flag it as a breaking change in the PR description. - Mark experimental APIs with the MkDocs Material
!!! warningadmonition in their docstring.
The repo uses langchain_core._api deprecation utilities — surface_langchain_deprecation_warnings, surface_langchain_beta_warnings, LangChainDeprecationWarning, LangChainBetaWarning — to flag pending removals at import time.
Type hints everywhere
All Python code must include type hints and return types. langchain-core and langchain are checked under mypy in strict = true with enable_error_code = "deprecated". Specific patterns:
from __future__ import annotationsis not universally used; many files prefer fully evaluated annotations because Pydantic validators need them.- Generic type variables are declared at the top of the file (
TypeVar("T"),TypeVar("ResponseT")). TYPE_CHECKINGguards for heavy imports (e.g.langgraph.graph.state.CompiledStateGraph) so that runtime import cost stays low.Annotated[T, OmitFromSchema]is used in agent state definitions to control what shows up in the public schema.
Docstrings
Google-style with an Args: section (and Returns:, Raises: where relevant). Examples:
def trim_messages(
messages: list[AnyMessage],
*,
max_tokens: int,
token_counter: Callable[[list[AnyMessage]], int],
) -> list[AnyMessage]:
"""Trim a message list to fit within a token budget.
Args:
messages: The messages to trim, oldest first.
max_tokens: Maximum allowed token count.
token_counter: Function returning a token count for a message list.
Returns:
A truncated message list that fits within the budget.
Raises:
ValueError: If `max_tokens` is non-positive.
"""Conventions:
- Types live in the signature, not in the docstring.
- Don't repeat the default value in the docstring unless it is post-processed or set conditionally.
- Use single backticks for inline code (
`tool_call`), never Sphinx-style double backticks. - Use American English spelling.
Error handling
- No bare
except:— always catch a specific exception type. - Use a
msgvariable for the exception text and pass it in:msg = f"..."; raise ValueError(msg). This is what the project'sruffconfig enforces (EM101,EM102are disabled in select files but flagged everywhere else). - No
eval(),exec(), orpickleon user-controlled input — see the_security/module inlangchain-corefor the policy hooks. - Don't leak resources: use context managers for files, sockets, and HTTP clients.
Import discipline
- No relative imports anywhere (
ban-relative-imports = "all"in every ruff config). - Heavy or optional dependencies use lazy imports. Examples:
libs/langchain_v1/langchain/chat_models/base.pydoesimportlib.import_module(module_path)insideinit_chat_modelso a user withoutlangchain_anthropicinstalled can still use OpenAI.libs/core/langchain_core/runnables/__init__.pydeclares a_dynamic_importsmap and overrides__getattr__to import on first access.
- Re-exports happen at the package root
__init__.py, never via*.
Pydantic patterns
- Pydantic v2 throughout.
pyproject.tomlpinspydantic>=2.7.4,<3.0.0. - Custom validators use
@field_validatorand@model_validator(not the v1@validator). - Some classes use
langchain_core.utils.pydantic.pre_init(registered as a classmethod-decorator in ruff config) for backwards-compatible initialization shimming.
File reference style in docs
When referencing source files in prose or docstrings, use full paths from the repo root:
- Good:
libs/core/langchain_core/runnables/base.py - Bad:
base.py(ambiguous; broken link in rendered docs)
This is enforced by reviewers; the wiki tooling and the API reference site assume full paths.
Conventional commits
PR titles are linted. Format: <type>(<scope>): <description>.
- Allowed types and scopes are listed in
.github/workflows/pr_lint.yml. - The description starts with a lowercase letter unless it's a proper noun or backtick-wrapped named entity.
- Wrap class/function/parameter names in backticks.
Example titles from recent history:
feat(langchain): add new chat completion feature
fix(core): resolve type hinting issue in vector store
chore(anthropic): update infrastructure dependencies
fix(openai): infer Azure chat profiles from model nameFile organization within a package
| Path | Convention |
|---|---|
<pkg>/<pkg>/__init__.py |
Re-exports the public API only |
<pkg>/<pkg>/_internal/ or _<name>.py |
Private modules — the leading underscore signals "do not import from outside this package" |
<pkg>/<pkg>/data/ |
Static JSON/TOML data (e.g. partner model profiles) |
<pkg>/<pkg>/py.typed |
Empty marker file declaring PEP 561 type-info availability |
<pkg>/scripts/ |
Maintenance scripts, not part of the package |
<pkg>/tests/unit_tests/ |
No-network tests |
<pkg>/tests/integration_tests/ |
Network-allowed tests |
Test files mirror the source layout: tests/unit_tests/messages/test_ai.py covers langchain_core/messages/ai.py.
Model references in code and docs
Always cite the latest GA models in docstrings and example snippets. Avoid preview/beta IDs unless they are the only option. Verify model IDs against the provider's official docs before committing — model lists in this repo go stale quickly because providers ship new models constantly.
For machine-readable capability data (context window, tool support, multimodal flags), use the langchain-profiles CLI in libs/model-profiles/langchain_model_profiles/cli.py. Each partner's data/ directory holds the resulting JSON.
Cross-cutting "danger zones"
libs/core/langchain_core/runnables/base.py— every change here touches every downstream caller. Be especially careful with new generic parameters or async semantics.libs/core/langchain_core/messages/content.py— adding a new content-block type means every partner needs a translator update.libs/langchain_v1/langchain/agents/middleware/types.py— theAgentMiddlewarebase class signature is part of the middleware ABI; changing it ripples through every built-in middleware and every external middleware author.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.