This section covers the developer experience for working within the LangChain monorepo: how the repository is laid out, what tools are used, how code quality is enforced, and how changes are reviewed and contributed.
For information about the CI/CD pipeline and automated testing infrastructure, see CI/CD and Testing Infrastructure. For the package release lifecycle specifically, see Release Process and Workflows.
The repository is a single monorepo containing multiple independently versioned Python packages, all living under libs/. Each package has its own pyproject.toml, uv.lock, and Makefile.
langchain/
├── libs/
│ ├── core/ # langchain-core: base abstractions
│ ├── langchain/ # langchain-classic: legacy package (no new features)
│ ├── langchain_v1/ # langchain: actively maintained package
│ ├── partners/
│ │ ├── openai/
│ │ ├── anthropic/
│ │ ├── ollama/
│ │ └── ...
│ ├── text-splitters/ # langchain-text-splitters
│ ├── standard-tests/ # langchain-tests: shared test suites
│ └── model-profiles/ # model configuration profiles
├── .github/ # CI/CD workflows, PR templates, issue templates
├── .devcontainer/ # Dev container configuration
├── .pre-commit-config.yaml
├── CLAUDE.md # Development guidelines (AI agent context)
├── AGENTS.md # Same as CLAUDE.md (kept in sync)
└── README.md
Sources: CLAUDE.md1-28 AGENTS.md1-28
Every package in the monorepo uses the same set of tools consistently.
| Tool | Role | Config File |
|---|---|---|
uv | Dependency management and virtual environments | pyproject.toml, uv.lock |
hatchling | PEP 517 build backend | pyproject.toml [build-system] |
ruff | Linting and code formatting | pyproject.toml [tool.ruff] |
mypy | Static type checking | pyproject.toml [tool.mypy] |
pytest | Test runner | pyproject.toml [tool.pytest.ini_options] |
make | Task runner wrapping the above | Makefile per package |
pre-commit | Git hook enforcement | .pre-commit-config.yaml |
Sources: CLAUDE.md36-43 libs/core/pyproject.toml1-3 libs/langchain/pyproject.toml1-3
Each pyproject.toml uses [dependency-groups] (a uv feature) to separate concerns. The groups are consistent across packages:
Dependency group overview for langchain-core:
[dependency-groups]
lint → ruff
typing → mypy, type stubs, langchain-text-splitters
dev → jupyter, setuptools, grandalf
test → pytest + plugins, langchain-tests, numpy, blockbuster
test_integration → (empty for core; populated in partner packages)
Common make targets that use these groups:
| Command | Group Used | Purpose |
|---|---|---|
make format | lint | Runs ruff format |
make lint | lint | Runs ruff check |
uv run --group lint mypy . | typing | Type-checks the package |
make test | test | Runs unit tests |
make test_integration | test_integration | Runs integration tests |
To install all groups before running tests:
Sources: libs/core/pyproject.toml47-78 libs/langchain/pyproject.toml65-128 CLAUDE.md47-74
Within the monorepo, packages reference each other via [tool.uv.sources] using editable local paths instead of PyPI. This allows changes in one package to be immediately visible to dependents during development.
Example from libs/core/pyproject.toml:
Example from libs/langchain_v1/pyproject.toml:
Sources: libs/core/pyproject.toml80-82 libs/langchain_v1/pyproject.toml97-102
The following diagram shows how the core tools interact during a typical development workflow.
Developer workflow: from code change to passing CI
Sources: .pre-commit-config.yaml1-126 CLAUDE.md36-74
The .pre-commit-config.yaml at the root defines hooks that run on every git commit. Hooks are organized into three categories:
General file hygiene (from pre-commit-hooks):
| Hook | Purpose |
|---|---|
no-commit-to-branch | Prevents direct commits to master |
check-yaml | Validates YAML syntax |
check-toml | Validates TOML syntax |
end-of-file-fixer | Ensures files end with a newline |
trailing-whitespace | Removes trailing whitespace |
Text normalization (from texthooks):
| Hook | Purpose |
|---|---|
fix-smartquotes | Replaces curly quotes with straight quotes |
fix-spaces | Replaces non-standard spaces |
Per-package format and lint hooks:
Each package in libs/ has a local hook that runs make -C libs/<package> format lint when files in that package change. Packages covered include: core, langchain, standard-tests, text-splitters, and all partner packages (anthropic, chroma, exa, fireworks, groq, huggingface, mistralai, nomic, ollama, openai, qdrant).
Version consistency hooks:
| Hook | Trigger Files | What It Checks |
|---|---|---|
core-version | libs/core/pyproject.toml, libs/core/langchain_core/version.py | Both files declare the same version |
langchain-v1-version | libs/langchain_v1/pyproject.toml, libs/langchain_v1/langchain/__init__.py | Both files declare the same version |
Sources: .pre-commit-config.yaml1-126
All packages configure ruff with a consistent baseline in their pyproject.toml. The key settings:
select = ["ALL"] — all rule categories enabled by default| Rule Code | Category | Reason Ignored |
|---|---|---|
C90 | McCabe complexity | Not enforced |
COM812 | Trailing comma | Conflicts with formatter |
FIX002 | TODO comments | Allowed in codebase |
PLR09 | Too many arguments/statements | Not enforced |
TD002, TD003 | TODO author/link | Not required |
ANN401 | No Any types | Still in use |
ban-relative-imports = "all" — all imports must be absoluteconvention = "google" — Google-style docstrings enforced by pydocstyledocstring-code-format = true — code blocks in docstrings are formattedPer-file overrides relax rules for test files (no docstring requirements, assertions allowed, magic numbers allowed) and script files.
Sources: libs/core/pyproject.toml94-149 libs/langchain_v1/pyproject.toml104-181
Type checking uses mypy in strict mode with the pydantic.mypy plugin enabled. Key settings shared across packages:
The pydantic.mypy plugin is required because Pydantic models use metaclass magic that plain mypy cannot resolve. The enable_error_code = "deprecated" setting causes mypy to flag uses of @deprecated-marked symbols.
Sources: libs/core/pyproject.toml85-92 libs/langchain/pyproject.toml142-151 libs/langchain_v1/pyproject.toml107-117
Each package configures pytest in [tool.pytest.ini_options]. The standard configuration across packages:
The requires marker allows conditional test skipping when a library is not installed. The implementation is in conftest.py across packages, which inspects util.find_spec for each required package name. See Pytest Configuration and Fixtures for full details.
Sources: libs/langchain_v1/pyproject.toml185-197 libs/langchain/tests/unit_tests/conftest.py1-99
The langchain-classic package includes test_dependencies.py which explicitly asserts the exact set of required and test-group dependencies. This acts as a guardrail: any PR that adds a new dependency to pyproject.toml will fail this test and must be intentional.
The test reads the live pyproject.toml at test time and compares the dependency list to a hard-coded expected set:
test_required_dependencies — asserts only PyYAML, SQLAlchemy, langchain-core, langchain-text-splitters, langsmith, pydantic, requests are in [project.dependencies]test_test_group_dependencies — asserts only known test tooling packages are in [dependency-groups.test]Sources: libs/langchain/tests/unit_tests/test_dependencies.py1-84
Each package that maintains a version constant in Python source must keep it synchronized with pyproject.toml. The approach varies by package:
langchain-core:
pyproject.toml declares version = "1.2.16"libs/core/langchain_core/version.py declares VERSION = "1.2.16"core-version pre-commit hook enforces they matchlangchain (langchain_v1):
pyproject.toml declares version = "1.2.10"libs/langchain_v1/langchain/__init__.py declares __version__ = "1.2.10"langchain-v1-version pre-commit hook and test_version_matches_pyproject unit test both enforce consistencySources: libs/core/langchain_core/version.py1-3 libs/langchain_v1/langchain/__init__.py1-3 libs/langchain_v1/tests/unit_tests/test_version.py1-27 .pre-commit-config.yaml114-125
PR titles are validated by the pr_lint.yml workflow using the amannn/action-semantic-pull-request action. The format is:
<type>(<scope>): <description>
Allowed types:
| Type | Meaning |
|---|---|
feat | New feature (MINOR bump) |
fix | Bug fix (PATCH bump) |
docs | Documentation changes only |
style | Formatting, linting, no logic change |
refactor | Code restructure without feature/fix |
perf | Performance improvement |
test | Adding or correcting tests |
build | Build system or dependency changes |
ci | CI/CD configuration changes |
chore | Miscellaneous maintenance |
revert | Reverts a previous commit |
release | Version bump commits |
Allowed scopes include: core, langchain, langchain-classic, standard-tests, text-splitters, anthropic, chroma, deepseek, exa, fireworks, groq, huggingface, mistralai, nomic, ollama, openai, perplexity, qdrant, xai, infra, deps.
A scope is required for all PRs. Even changes to the main
langchainpackage must usefeat(langchain):not justfeat:.
Sources: .github/workflows/pr_lint.yml1-117 CLAUDE.md83-93
From .github/PULL_REQUEST_TEMPLATE.md and CLAUDE.md:
make format, make lint, and make test from the root of each modified package before submitting.uv.lock or add dependencies to pyproject.toml without maintainer approval.Sources: .github/PULL_REQUEST_TEMPLATE.md1-38 CLAUDE.md94-99
Required in all contributed Python code:
Args:, Returns:, and Raises: sections for all public functionseval(), exec(), or pickle on user-controlled inputexcept: clauses; use a msg variable for error stringsSources: CLAUDE.md117-195
The repository ships a dev container configuration at .devcontainer/ for use with GitHub Codespaces or VS Code Dev Containers.
Container setup (devcontainer.json):
libs/langchain/dev.Dockerfile via docker-compose.yamlcd libs/langchain_v1 && uv syncms-python.python, ms-python.debugpy, ms-python.mypy-type-checker, ms-toolsai.jupyter, GitHub.copilotlibs/langchain_v1/.venv/bin/pythonSources: .devcontainer/devcontainer.json1-58 .devcontainer/docker-compose.yaml1-12
The .github/CODEOWNERS file defines review requirements:
| Path | Required Reviewers |
|---|---|
/.github/ | @ccurme, @eyurtsev, @mdrxy |
/libs/core/ | @eyurtsev |
/libs/partners/ | @ccurme, @mdrxy |
Sources: .github/CODEOWNERS1-4
Sources: (derived from table of contents structure)
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.