## Description
Adds an "Add the `managed-deep-agents` skill" section to the Managed
Deep Agents quickstart, between Prerequisites and the build steps.
Readers who build with a coding agent get better results with the
[`managed-deep-agents`
skill](https://github.com/langchain-ai/langchain-skills/blob/main/config/skills/managed-deep-agents/SKILL.md)
installed, so the section links the skill and gives the `npx skills add
langchain-ai/langchain-skills --skill managed-deep-agents --yes` install
command.
Placement is the main thing worth a look: the skill is most useful
before the reader starts scaffolding, but it can move to Next steps if
that reads better.
Docs-only change; no new pages, so `src/docs.json` is untouched. `make
lint_prose` passes on the changed file. Written by an AI agent (Open
SWE) at the requester's direction.
## Test Plan
- [ ] Preview the quickstart page and confirm the new section renders
with the heading in code formatting.
Made by [Open
SWE](https://openswe.vercel.app/agents/25e9b868-246d-5f0a-afc6-dffbd06e42a5)
---------
Co-authored-by: Caspar Broekhuizen <25157475+casparb@users.noreply.github.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Two entries in the sidebar navigation (`src/docs.json`) point to pages
that
immediately redirect the user elsewhere:
- `oss/python/langchain/retrieval` redirects to
`/oss/python/deepagents/retrieval`
- `oss/javascript/langchain/retrieval` redirects to
`/oss/javascript/deepagents/retrieval`
The content was moved to the `deepagents/` path and redirects were added
correctly, but the navigation was not updated. Every user clicking the
nav
link gets an unnecessary redirect hop instead of landing directly on the
canonical page.
Fix: update the 2 nav entries in `src/docs.json` to point directly to
`oss/python/deepagents/retrieval` and
`oss/javascript/deepagents/retrieval`.
Verified with a full pipeline build (no warnings, JSON valid). Fix
ready.
Fixes DOC-1544
## Summary
- Added a `<Note>` callout after the `helm upgrade` command in the
Kubernetes self-hosted deployment guide explaining that the namespace
must already exist before running the command
- Instructs users to either pre-create the namespace with `kubectl
create namespace <namespace>` or append `--create-namespace` to the helm
command
## Links
- Linear: https://linear.app/langchain/issue/DOC-1544
- Slack:
https://langchain.slack.com/archives/C09G1T60QV9/p1787010227131159
## Verification
- `make lint_prose` — no errors in `kubernetes.mdx` (pre-existing errors
in other files are unrelated)
- `make check-cross-refs` — ✅ all cross-references resolved
## Reviewers
Requested review from: @katmayb, @fjmorris
Co-authored-by: Docs Bot <brace@langchain.dev>
## Summary
Automated daily refresh of the LangSmith Platform API spec.
## Details
- Fetched latest spec from api.smith.langchain.com
- Applied post-processing: hid fleet/internal endpoints, added
human-readable group tags
- Output: `src/langsmith/langsmith-platform-openapi.json`
- Generated by `scripts/process_langsmith_openapi.py`
🤖 This PR was created automatically by GitHub Actions
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Fixes DOC-1523
## Summary
Documents the **Include extended stats** evaluator setting so users can
determine when to enable it and which data it makes available.
- Clarifies run-level support and the multi-turn evaluator limitation.
- Distinguishes `feedback_stats` from individual feedback records
available to code evaluators.
- Adds supported LLM-as-a-judge variable-mapping guidance and a
feedback-filtered chaining link.
## Validation
- `make lint_prose FILES='src/langsmith/evaluators.mdx
src/langsmith/online-evaluations-code.mdx'`
- `make broken-links-with-anchors`
## Notes
This PR was prepared with AI agent assistance and reviewed against the
LangSmith UI and backend implementation.
---------
Co-authored-by: Lauren Hirata Singh <lauren@langchain.dev>
## Summary
Rewrites one sentence in the `<Info>` block of the "Short-term memory
(State)" section on the Python tools page
(`src/oss/langchain/tools.mdx`).
Old:
> Add `runtime: ToolRuntime` to your tool signature to access state.
This parameter is automatically injected and hidden from the LLM - it
won't appear in the tool's schema.
New:
> To access state, add `runtime: ToolRuntime` to your tool signature.
When the tool runs, the runtime (for example, `ToolNode`) supplies the
value, not the model, and the parameter is omitted from the tool schema
sent to the model.
## Why
"Automatically injected" is ambiguous about who acts. Injection is
opt-in: the user declares the `runtime: ToolRuntime` parameter
themselves. What is automatic is that, once declared, the value is
supplied at call time by the runtime (e.g. `ToolNode`) rather than
generated by the model, and the parameter is omitted from the
model-facing tool schema. The new wording makes that distinction
explicit and matches the "hidden from the model" phrasing used in the
`<Warning>` further down the same page.
No other content changed. Vale prose lint passes on the edited file.
---
AI-assisted change (reviewed and verified against the repo's style
conventions).
---------
Co-authored-by: Docs Bot <brace@langchain.dev>
The sandbox pages had accumulated claims the implementation no longer
honors. Some are cosmetic; several actively break anyone who copies
them. Each item below was checked against `smith-go/sandboxes`, the
LangSmith CLI, and the SDKs.
## Wrong in a way that breaks users
- **`wait_for_ready` removed from the REST examples.** The field was
deleted from `CreateSandboxPayload` and is now silently ignored.
Creating a sandbox already boots it and returns once it reports `ready`,
so the parameter is redundant rather than load-bearing. The name
survives as a *client-side* SDK kwarg (default `True`), which is
probably how the REST examples drifted.
- **CLI `--wait` and the `wait` subcommands removed.** `--wait`,
`--timeout`, `sandbox wait`, and `snapshot wait` no longer exist, for
the same reason: `create` returns ready. Snapshot builds are genuinely
asynchronous, so those keep a documented poll.
- **CLI output default corrected.** It is human-readable tables, with
`--format json` for scripting; the docs had it backwards. Also fixed on
the CLI overview and profile pages.
- **GCS mount `scopes` removed from the examples.** The SDK drops the
field when building `mount_config.auth.gcp` and the backend rejects it
outright, deriving the scope from each mount's `read_only`. Also
documents that one `mount_config` cannot mix read-only and writable GCS
mounts.
## Wrong numbers and facts
- **Running-command idle timeout is 1 hour**, not 5 minutes. Five
minutes is the finished-session TTL, which was previously left
unnumbered and is now stated.
- **Builder sandbox default is 0.5 vCPU**, not "a single core".
- **The 2-vCPU Dockerfile builder example** paired 2 vCPU with 4 GiB,
landing exactly on the edge of the 4 GiB per vCPU tolerance. Now 8 GiB,
matching the JS SDK README.
- **AWS and GCP auth-rule limits count disabled rules too**, so "at most
one enabled GCP rule" was wrong.
- **`/tmp` is not a tmpfs.** Only `/dev/shm` is, so `/tmp` lives on the
sandbox disk and is captured. The old note claimed the opposite while
the stop/start example on the same page relied on `/tmp` persisting.
## Reachable but undocumented
Memory snapshots (`include_memory`, `restore_memory`,
`preserve_memory_on_stop`), the CPU/memory/filesystem limits, labels,
and the CLI `--vcpus` / `--memory` sizing flags. Memory snapshots are
documented as REST-only, since the `langsmith.sandbox` clients do not
expose them.
## Stop and start
Sandboxes wake on their next request, so the docs no longer walk users
through starting one by hand. Explicit `start` is gone from the prose,
examples, and the CLI command reference; `stop` stays where releasing
resources early is the point. The `start` API and CLI command still
exist, they are just no longer presented as a step you take.
## Worth a careful look
- The `/tmp` claim is the one I could not confirm on a live sandbox,
because the only endpoint configured locally is production. It rests on
three code sources agreeing: the guest init mounts only `/dev/shm` as
tmpfs, the rootfs image adds no `/tmp` tmpfs, and `e2e/snapshot_test.go`
says the same in a comment. Happy to confirm on a real box if you would
rather not take the code's word for it.
- The GCS `scopes` behavior reads as a product bug rather than a doc
bug, and is documented as-is here: the SDK silently drops a field the
backend would have rejected.
## Test plan
- [x] `make lint_prose` clean on all changed files
- [x] `make build` succeeds; the one new anchor (`#resume-from-memory`)
resolves
- [x] Every changed claim traced to the implementation in
`smith-go/sandboxes`, `langsmith-cli`, or `langsmith-sdk`
Written with Claude Code.
`src/oss/deepagents/backends.mdx` contains 22 em dashes written as
`word — word` (with surrounding spaces), which violates the
`LangChain.DashesSpaces` Vale rule enforced by `make lint_prose` (CI).
Per AGENTS.md: "Add spaces around em dashes — write word—word not
word — word (make lint_prose enforces this)."
Fix: replace all 22 instances of ` — ` with `—` in prose lines
(the 2 instances inside code block comments are untouched).
1 file changed, 23 lines. Pipeline build clean. Fix ready.
## Summary
Automated update of package download statistics from pepy.tech and npm
## Details
- Updates download counts in `packages.yml`
- Regenerates provider overview page at
`src/oss/python/integrations/providers/overview.mdx`
- Regenerates integration download tables under `src/snippets/oss/`
- Generated by GitHub Actions workflow `update-package-downloads.yml`
- Scheduled to run every Sunday at 11:59 PM UTC
🤖 This PR was created automatically by GitHub Actions
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Lauren Hirata Singh <lauren@langchain.dev>
## Why
Ten self-hosted releases published with only the `Internal improvements
and maintenance updates` placeholder even though they shipped real
customer-facing changes. This restores 33 notes.
**Root cause:** the deployed `helm-changelog-bot` runs code from before
2026-06-25, so it only scrapes the legacy `## Self-Hosted Release Note`
PR-body section. Three features merged that day were never deployed (the
repo has no CI, deploys are manual):
- `f3cd4f3` read `.changelog/*.yaml` fragments
- `2537c64` PR-title fallback for fragment-less PRs
- `2bbda07` honor the `skip-changelog` label
As `langchainplus` migrated to fragments, every fragment-sourced note
was silently dropped. Evidence: langsmith-0.16.1 published the polished
*legacy* Release Note from langchainplus#33020, not that PR's fragment
body.
## What changed
Notes re-derived from the fragments and PR titles, following the
resolution order in the bot's current `main`.
| Release | Notes restored |
|---|---|
| 0.16.7 | 1 |
| 0.16.2 | 1 |
| 0.16.0-rc.29 | 5 |
| 0.16.0-rc.28 | 4 |
| 0.16.0-rc.27 | 2 |
| 0.16.0-rc.24 | 6 |
| 0.16.0-rc.23 | 2 |
| 0.16.0-rc.22 | 1 |
| 0.16.0-rc.16 | 10 |
| 0.17.0-rc.1 | 1 |
The fix propagates through the image-dedup links: 0.16.3 and 0.16.4
point at 0.16.2, and 0.17.0-rc.2 through rc.6 point at 0.17.0-rc.1.
## Please review carefully
**Wording.** These were condensed from fragment bodies by hand, not by
the bot's LLM polish step. Past tense per the changelog's established
voice.
**Deliberate exclusions:**
- Two `status: held` fragments still gated behind Eppo flags
(`forge_issue_board_enabled` on 0.16.0-rc.29, `threads_aq` on
0.17.0-rc.1). Worth noting the bot does not check `status` at all, so
once redeployed it would publish gated features early.
- Notes referencing SmithDB, which was not available for self-hosted at
those versions. Matches `SELF_HOSTED_EXCLUDED_TERMS` and the precedent
in #4476.
- Raw internal PR titles with no customer meaning (CI pinning, token
minting, service co-hosting, internal flag gating).
**Untouched and correct:** 0.16.5, 0.16.0-rc.25, rc.20, rc.18, 0.15.17,
and 0.15.16 shipped only chore bumps or opted-out PRs, so their
placeholders are accurate.
## Follow-up outside this PR
1. Redeploy the bot from `main`, and add deploy automation so merges
cannot silently fail to ship again.
2. Teach `get_pr_release_notes` to skip fragments where `status !=
"ready"`.
3. Backport PRs into `v16-stable` carry `self_hosted: false` fragments
authored for Cloud (langchainplus#33187, #33719, and six rc.16 PRs), so
genuinely self-hosted changes are marked Cloud-only.
4. Security fixes are being labeled `skip-changelog`
(langchainplus#33480, vulnerable Python dependency bumps). Self-hosted
operators generally want those.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Fixes DOC-1540
## Summary
- Remove the word "normal" from the opening sentence on both the MDA
middleware and MDA tools pages
- `managed-deep-agents-middleware.mdx`: "the normal Deep Agents
`middleware` configuration surface" → "the Deep Agents `middleware`
configuration surface"
- `managed-deep-agents-tools.mdx`: "the normal Deep Agents `tools`
configuration surface" → "the Deep Agents `tools` configuration surface"
This was flagged by Eli Verdun as informal — "normal" implies an awkward
hierarchy. Eli suggested just saying "Deep Agents" directly.
## Links
- Linear:
https://linear.app/langchain/issue/DOC-1540/replace-normal-deep-agents-with-clearer-terminology-in-mda-middleware
- Slack:
https://langchain.slack.com/archives/C09G1T60QV9/p1786826676152809
## Verification
Not run; docs-only copy change (two single-word removals).
## Reviewers
Requested review from: @fjmorris, @katmayb
Co-authored-by: Docs Bot <docs-bot@langchain.dev>
Week covered: August 10-17, 2026
Source fragments:
- .changelog/2026-08-11-* (21 ready, 5 held)
- .changelog/2026-08-12-* (10 ready, 5 held)
- .changelog/2026-08-13-* (14 ready, 4 held)
- .changelog/2026-08-14-* (4 ready, 0 held)
Held or skipped:
- 14 held fragments stayed out of the published changelog because they
are still gated / not fully rolled out.
- 0 invalid fragments in this week’s range.
Agent involvement: drafted and published by an AI agent.
Co-authored-by: Lauren <lauren@langchain.dev>
Fixes DOC-1538
## Summary
- Follow AGENTS.md style guide: "Managed Deep Agents" (product name,
always capitalized) vs. "a managed deep agent" (common noun — an
instance the user creates, lowercase)
- Fix Tab title from title case `"Model & Configuration"` → sentence
case `"Model & configuration"` in `managed-deep-agents-overview.mdx`
(`#example-agent` section)
- Fix 5 other inline prose occurrences across MDA docs pages where the
singular instance was incorrectly capitalized as a proper noun
**Files changed:**
- `src/langsmith/managed-deep-agents-overview.mdx` — Tab title sentence
case
- `src/langsmith/managed-deep-agents-memory.mdx` — "a Managed Deep
Agent's" → "a managed deep agent's"
- `src/langsmith/managed-deep-agents-mcp-connectors.mdx` — "to a Managed
Deep Agent" → "to a managed deep agent"
- `src/langsmith/managed-deep-agents-channels.mdx` — "a Managed Deep
Agent to" → "a managed deep agent to"
- `src/langsmith/managed-deep-agents-channels-slack.mdx` — 2 instances
of "Managed Deep Agent" → "managed deep agent"
- `src/langsmith/managed-deep-agents-sandboxes.mdx` — "a Managed Deep
Agent" → "a managed deep agent"
## Links
- Linear:
https://linear.app/langchain/issue/DOC-1538/fix-sentence-case-for-managed-deep-agent-and-product-name
- Slack:
https://langchain.slack.com/archives/C09G1T60QV9/p1786826257729819
## Verification
- `make check-cross-refs` — ✅ All cross-references resolved
- `make lint_prose` — No errors in changed files (60 pre-existing errors
in other unrelated files)
## Reviewers
Requested review from: @lnhsingh, @fjmorris
---------
Co-authored-by: Docs Bot <docs-bot@langchain.dev>
## Description
The mirroring guide told users to use mutable `latest` tags, which can
lead to mixed service versions during releases. It now directs users to
the Helm chart `appVersion` and uses a concrete version in Docker
examples.
## Test Plan
- [x] Run Vale on the changed page
- [x] Verify the example image tag exists on Docker Hub
Made by [Open
SWE](https://openswe.vercel.app/agents/72c6367c-4a4b-9450-bc47-ab5f5754a7c2)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
## Overview
Update Gemini docs to use 3.7 Flash in examples.
## Type of change
**Type:** Update existing documentation
## Related issues/PRs
- Slack thread:
## Checklist
- [x] I have read the [contributing guidelines](README.md), including
the [language
policy](https://docs.langchain.com/oss/python/contributing/overview#language-policy)
- [x] I have tested my changes locally using `docs dev`
- [x] All code examples have been tested and work correctly
- [x] I have used **root relative** paths for internal links
- [x] I have updated navigation in `src/docs.json` if needed
(Internal team members only / optional): Create a preview deployment as
necessary using the [Create Preview Branch
workflow](https://github.com/langchain-ai/docs/actions/workflows/create-preview-branch.yml)
## Additional notes
<!-- Any other information that would be helpful for reviewers -->
## Summary
Removes the hardcoded "every 6 hours" scan-cadence claim from the
LangSmith Engine docs and describes recurring scans as running on a
dynamic schedule tuned to balance cost and performance instead.
- `src/langsmith/engine.mdx`: updated the LCU cost table's "Recurring
scans" trigger and the accompanying prose.
- `src/langsmith/engine-overview.mdx`: updated the "How Engine runs"
section.
## Why this approach?
The scan interval is an internal implementation detail that can change
over time to optimize cost and performance. Documenting a fixed 6-hour
interval creates a stale, overly specific commitment; describing the
cadence as dynamic keeps the docs accurate without needing a follow-up
edit whenever the interval changes.
## Test Plan
- [x] `make lint_prose FILES="src/langsmith/engine.mdx
src/langsmith/engine-overview.mdx"` — 0 errors, 0 warnings, 0
suggestions
- [x] Verified no other `.mdx` file in the repo references Engine's scan
interval as a fixed hour count
## Release Note
Updated the Engine docs to describe scan cadence as dynamic (tuned for
cost/performance) rather than a fixed 6-hour interval.
Fixes DOC-1333
## Summary
- Adds a new `### Add process-level resource attributes` section to the
OTel tracing docs (`src/langsmith/trace-with-opentelemetry.mdx`), under
the existing "Use environment variables for global configuration"
subsection.
- Explains how to use the standard `OTEL_RESOURCE_ATTRIBUTES` env var to
attach custom metadata (deployment environment, service version, team,
region, etc.) to all traces emitted by a process, without modifying
application code.
- Also adds `OTEL_RESOURCE_ATTRIBUTES` to the existing env var listing
code block.
## Links
- Linear:
https://linear.app/langchain/issue/DOC-1333/add-otel-resource-attributes-paragraph-to-otel-tracing-docs
- Slack:
https://langchain.slack.com/archives/C09G1T60QV9/p1783186731049369
## Verification
Cross-references checked: `make check-cross-refs` ✅. Prose linting
(Vale) not available in sandbox — docs-only copy change.
## Reviewers
Requested review from: @katmayb, @fjmorris
---------
Co-authored-by: Docs Writer Bot <brace@langchain.dev>
Co-authored-by: Kathryn May <44557882+katmayb@users.noreply.github.com>
Fixes DOC-1430
## Summary
- Expands the `return_direct` docs on parallel tool calls.
`create_agent` routes to `END` only when **every** client-side tool call
in the step has `return_direct=True`; all tools in the batch execute
first, and the final response carries every `ToolMessage`.
- Adds a `<Warning>` for mixed parallel calls: a `return_direct=True`
tool called alongside a normal tool does **not** exit the loop. The
agent routes back to the model with all results.
- Adds a Python-only "Return a Command with return_direct" subsection
covering `Command` returns from a `return_direct` tool, the
matching-`ToolMessage` requirement, and the `graph=Command.PARENT`
escape hatch.
- Corrects the pre-existing "Return a Command" section, which said a
`Command` could be returned "with or without including a `ToolMessage`".
A matching `ToolMessage` is required when the `Command` targets the
current graph.
- Scopes Python-only claims by language. `ToolNode` validation and the
`ValueError` exist only in Python, and the tool call ID accessor differs
between languages, so both now sit in `:::python` / `:::js` fences.
## Correction to an earlier revision of this description
An earlier version of this description claimed the agent exits when
**any** tool in a parallel batch has `return_direct=True`, citing
`chat_agent_executor.py`. That is the behavior of the legacy
`langgraph.prebuilt.create_react_agent`, not the
`langchain.agents.create_agent` this page documents. The two
implementations genuinely differ. The page's `all` wording is correct
and was never changed to `any`.
## Verification
Verified against `langchain-ai/langchain` `master` and
`langchain-ai/langgraph` `main` as of 2026-08-14.
**Parallel `return_direct` (`all`, not `any`):**
`libs/langchain_v1/langchain/agents/factory.py`,
`_make_tools_to_model_edge` → `tools_to_model`. Exit condition is
`client_side_tool_calls and
all(tool_node.tools_by_name[c["name"]].return_direct for c in
client_side_tool_calls)`. A mixed batch falls through to the default
case and routes back to the model.
For contrast, `langgraph`
`libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py`
`route_tool_responses` returns `END` as soon as one trailing
`ToolMessage` matches `should_return_direct`, with an `any(...)`
fallback for tools executed in a separate `Send`. That is
`create_react_agent`, a different entry point.
**`ToolMessage` requirement:** `langgraph`
`libs/prebuilt/langgraph/prebuilt/tool_node.py`,
`_validate_tool_command`. The `ValueError` is guarded by
`require_terminator and updated_command.graph is None and not
has_matching_tool_message`, which confirms both the requirement for the
current graph and that `graph=Command.PARENT` lifts it. Matching is on
`message.tool_call_id == call["id"]`.
**JavaScript:** `langgraphjs`
`libs/langgraph-core/src/prebuilt/tool_node.ts` has no equivalent of
`_validate_tool_command` and never raises on a missing `ToolMessage`, so
the `ValueError` claim is Python-only. `runtime.toolCallId` is confirmed
in `langchainjs` `libs/langchain/src/agents/tests/tools.test.ts`.
**Linting and build:** `make lint_prose` → 0 errors, 0 warnings. `make
build` → clean; the generated JS page keeps the shared requirement and
drops both Python-only blocks. `make check-cross-refs` → all
cross-references resolved.
## Links
- Linear:
https://linear.app/langchain/issue/DOC-1430/document-return-direct-behavior-gaps-on-oss-tools
- Slack:
https://langchain.slack.com/archives/C09G1T60QV9/p1784904894989719
## AI involvement
Drafted by an AI agent and revised by a second agent session, which
verified every behavioral claim against the langchain, langgraph,
langchainjs, and langgraphjs sources. Human review still needed.
## Reviewers
Requested review from: @npentrel, @lnhsingh
---------
Co-authored-by: Docs Bot <brace@langchain.dev>
Co-authored-by: Naomi Pentrel <5212232+npentrel@users.noreply.github.com>
Co-authored-by: Lauren Hirata Singh <lauren@langchain.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixes DOC-1527
## Summary
- Removes the "Supported in Python only" label from the configurable
base URLs / API keys accordion in the LLM Gateway quickstart
- Splits the supported chat models list into Python and TypeScript
sections
- Adds TypeScript providers: `@langchain/anthropic >= 1.5.4`,
`@langchain/fireworks >= 0.2.7`, `@langchain/openai >= 1.5.7`
- Notes the TypeScript env var (`OPENAI_BASE_URL`) alongside the Python
one (`OPENAI_API_BASE`) in the override example
- Updates the resolution table description to mention both `*_API_BASE`
(Python) and `*_BASE_URL` (TypeScript) patterns
## Links
- Linear:
https://linear.app/langchain/issue/DOC-1527/update-llm-gateway-quickstart-configurable-base-urls-and-api-keys-now
- Slack:
https://langchain.slack.com/archives/C09G1T60QV9/p1786578739338799
## Verification
Verified JS gateway support directly in `langchain-ai/langchainjs`
source:
- `@langchain/core/src/utils/gateway.ts` —
`resolveLangSmithGatewayConfig` handles `LANGSMITH_GATEWAY` /
`LANGSMITH_GATEWAY_API_KEY`
- `@langchain/openai`, `@langchain/anthropic`, `@langchain/fireworks`
all call `resolveLangSmithGatewayConfig` and respect provider-specific
base URL env vars as overrides
- Google Gemini and Baseten are not yet supported in JS (not included)
## Reviewers
Requested review from: @katmayb, @fjmorris
---------
Co-authored-by: Docs Writer <brace@langchain.dev>
Co-authored-by: Lauren Hirata Singh <lauren@langchain.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixes DOC-1529
## Summary
- Renamed the section heading `## Additional images for Fleet and
Insights` → `## Additional images for Fleet` on the self-host mirroring
images page
- Updated the opening sentence of that section to remove the reference
to "Insights", since Insights is now part of `langsmith-insights-engine`
and is already documented in the "Additional images for Engine" section
immediately above
## Links
- Linear: https://linear.app/langchain/issue/DOC-1529
- Slack:
https://langchain.slack.com/archives/C09G1T60QV9/p1786595268005789
## Verification
No new prose lint errors introduced. No pages moved or deleted.
## Reviewers
Requested review from: @katmayb, @fjmorris
---------
Co-authored-by: Docs Bot <brace@langchain.dev>
Co-authored-by: Lauren Hirata Singh <lauren@langchain.dev>
## Why
Coverage fell from ~100% to **57%** after #5514. This is my regression,
and the cause is a Mintlify behavior I assumed rather than tested.
**Mintlify serves the exact filename `llms.txt` at any path, and 404s on
anything else.** The numbered variants my split produced were never
reachable:
| URL | status |
|---|---|
| `/oss/python/llms.txt` | 200 |
| `/oss/python/llms-2.txt` | **404** |
| `/oss/python/llms-3.txt` | **404** |
| `/langsmith/smith-api/llms.txt` | 200 |
| `/langsmith/smith-api/llms-2.txt` | **404** |
Only 4 of 9 section indexes were reachable. That left 1,229 visible
links — 1,112 across the four served files plus 117 inline in the root —
which is exactly what the audit reported: 883 sitemap pages plus 346
links not in the sitemap. The other **802 pages did not exist as far as
any agent was concerned**.
Confirmed the rule is filename-based, not path-based:
`.well-known/security.txt` is in the build and 404s, while
`/oss/python/llms.txt` byte-matches the file my build produced, so
Mintlify is serving my static file by name.
## What changed
Sections now split **by directory** rather than by filename. Every index
is written as `<prefix>/llms.txt`.
An oversized section sheds its heaviest child directories into their own
indexes until the remainder fits, instead of giving every child its own
file — a naive recursive split produced 109 files, most of them a single
page. Descriptions are also dropped from section indexes, which roughly
halves each entry; the root keeps them, since it has room.
| | before | after |
|---|---|---|
| root | 16,449 chars | 21,656 chars |
| section indexes | 9 (5 unreachable) | 56 (all reachable) |
| largest section | 40,101 | 42,263 |
| pages indexed | 2,038 (1,229 visible) | 2,038 |
All 56 are named `llms.txt`, none exceed 50,000 characters, and each is
one hop from the root so the coverage walker still reaches everything.
## The real gap
Every guard I added in #5514 checked **local files**, and nothing
checked they were **served**. The build-time validator passed happily
while a third of the index was unreachable in production.
`scripts/check_llms_urls.py` now verifies each section index returns 200
before it samples page URLs. Run against production today it correctly
reports 52 of 56 unserved, because the new paths are not deployed yet —
that is the check working. A unit test asserts no index is ever named
`llms-N.txt`.
## Still open: LLMS Full Size (a Mintlify routing issue, not a repo one)
The custom `llms-full.txt` **is** being picked up — Mintlify just serves
it on the wrong route. Probing all four endpoints:
| Route | Bytes | Whose |
|---|---:|---|
| `/llms.txt` | 16,449 | ours |
| `/.well-known/llms.txt` | 99,940 | Mintlify's, **truncated** |
| `/llms-full.txt` | 15,101,956 | Mintlify's |
| `/.well-known/llms-full.txt` | 6,246,717 | ours |
The override is applied to exactly one route per file, and they are
crossed. Verified by content rather than size:
`/.well-known/llms-full.txt` opens with `# Docs by LangChain`, while
`/.well-known/llms.txt` still ends with `_Note: this index was truncated
to stay under 100,000 characters; 569 pages and 3 OpenAPI specs
omitted._`. Neither `.well-known` file exists in our build (it contains
only `security.txt`), so both are served by Mintlify's own routing.
Two consequences:
1. **`LLMS Full Size` will keep failing** while the check reads
`/llms-full.txt`, because that route serves Mintlify's 15.1 MB file
regardless of the custom one. Nothing in this repo changes that.
2. **`/.well-known/llms.txt` serves a truncated index** that omits 569
pages, so any agent following the `.well-known` convention gets the
pre-fix file even though the root is correct.
This looks like a Mintlify bug and needs a support ticket, not a code
change here. Their documentation does not describe how custom files
interact with the `.well-known` mirrors. Separately, **no target size
for `llms-full.txt` is documented** by AFDocs or Mintlify, so even with
the routing fixed I cannot say whether 6.2 MB clears the bar.
## Other checks
- **Markdown Content Parity** (warning): unchanged diagnosis, not a
content defect. The only substantive gap is OpenAPI pages, where the
markdown is *richer* than the HTML. Set both parity thresholds to 0 for
informational mode.
- **LLMS TXT Directive Html** (1 of 15 pages), **Page Size Html** (1
page at 59K markdown from 1,195K HTML, 98% boilerplate), **Redirect
Behavior** (1 cross-host redirect): all single-page warnings in
Mintlify-controlled chrome or intentional `reference.langchain.com`
redirects. No repo change proposed.
- **Content Structure** and **Authentication**: skipped, not failures.
Both score 100.
## Validation
- 215 tests pass (1 new)
- `make lint` clean, `make broken-links` clean
- 56 section indexes, 0 named `llms-N.txt`, 0 over 50,000 chars
- 2,038 pages indexed, verified no duplicates and none lost
## AI disclosure
Authored with Claude Code (Claude Opus 5). The serving rule was
established by probing production rather than inferred, after the
previous PR shipped on an untested assumption about exactly this.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Why
Follow-up to #5504. The custom `llms.txt` that PR added was 286,685
characters, which fails the AFDocs `llms-txt-size` check. This is my
regression: I chose a single large file because I could not verify at
the time whether the checker followed nested indexes. It does — the
check output and the AFDocs reference both specify the nested shape.
## LLMS TXT Size — fail → pass
**Authoritative thresholds** (from the [AFDocs check
reference](https://afdocs.dev/checks/content-discoverability)):
| Result | Condition |
|---|---|
| Pass | Under **50,000** characters |
| Warn | 50,000–100,000 |
| Fail | Over 100,000 |
Note the pass bar is 50 K, not 100 K.
**Size is not the only cost.** An oversized index triggers the AFDocs
[index truncation
coefficient](https://afdocs.dev/agent-score-calculation#index-truncation-coefficient),
which discounts `llms-txt-valid`, `llms-txt-links-resolve`,
`llms-txt-links-markdown`, and `llms-txt-coverage` in proportion to the
visible fraction. At 286,685 characters that is roughly **0.35** — four
*passing* checks were contributing about a third of their weight. Fixing
size restores them to 1.0, so this is worth more than the single failing
check.
### What changed
`_generate_llms_txt` in `pipeline/core/builder.py` now emits a root
index plus section indexes.
Root: **16,449 characters** (was 286,685), listing 9 section indexes and
the 117 pages belonging to no large section.
| File | chars | entries |
|---|---:|---:|
| `/llms.txt` (root) | 16,449 | 117 inline + 9 section links |
| `/oss/python/llms.txt` | 39,983 | 236 |
| `/oss/python/llms-2.txt` | 40,101 | 255 |
| `/oss/python/llms-3.txt` | 5,310 | 47 |
| `/oss/javascript/llms.txt` | 39,957 | 236 |
| `/oss/javascript/llms-2.txt` | 15,778 | 121 |
| `/langsmith/llms.txt` | 39,999 | 271 |
| `/langsmith/llms-2.txt` | 30,851 | 219 |
| `/langsmith/smith-api/llms.txt` | 40,018 | 369 |
| `/langsmith/smith-api/llms-2.txt` | 20,214 | 167 |
Section files target 40 K rather than 50 K so they do not drift over the
threshold as pages are added between splits.
### Three constraints this is built around
- **Coverage must not regress.** AFDocs's coverage walker descends
*exactly one level* into linked `.txt` files, and treats `.txt` files
found at that depth as "omitted subtrees" whose pages leave the coverage
denominator. Every section index is therefore one hop from the root and
contains **zero** further `.txt` links, so all 2,038 pages stay directly
verified. Coverage is currently ~100% and stays there.
- **Link sampling must still see pages.** A root containing only `.txt`
links could starve `llms-txt-links-markdown` and
`llms-txt-links-resolve`. The root keeps 117 real `.md` links (the small
sections, inlined rather than split out) so both checks sample real
pages from the canonical file.
- **Section paths are derived, not hardcoded.** Each section's directory
comes from the deepest directory shared by its pages. An earlier
hardcoded map would have inlined the 60 K OpenAPI group into the root,
defeating the split.
### Verification
- 2,038 `.md` entries across root and sections, **0 duplicates, 0 pages
lost**
- Every file under 50,000 characters
- 0 nested `.txt` links inside section files
- 207 tests pass (1 new, covering root size, one-hop depth, no deeper
nesting, and exact page conservation)
- `make lint` clean, `make broken-links` clean
## Also in this PR
### LLMS Full Size — split into per-language corpora
The combined corpus was 15,050,368 characters. Roughly half was the
Python and TypeScript renders of the same documentation, so the build
now emits three corpora, with the root pointing at the other two:
| File | chars |
|---|---:|
| `/llms-full.txt` | 6,246,717 |
| `/oss/python/llms-full.txt` | 6,801,843 |
| `/oss/javascript/llms-full.txt` | 4,655,598 |
Root is **down 58.5%** from what the check currently sees.
**This requires expanding snippets ourselves.** Mintlify expands snippet
imports at render, so a corpus assembled from the raw build tree would
silently lose content from the 308 pages that import one. Verified page
by page against the published corpus: expansion lands 8–10% *under*
Mintlify's for snippet-heavy pages (`deepagents/customization` 139,256
vs 152,958; `langchain/agents` 64,279 vs 67,990), and only 7 of 1,525
shared pages come out more than 30% larger — all pages that have
genuinely grown since that snapshot.
The combined total exceeds Mintlify's because this covers every built
page (2,038) rather than only the sitemap subset (1,586). The extra
pages are integration pages that are reachable but not in navigation,
which `llms.txt` already indexes; excluding them here would make the two
files disagree about what exists.
Both custom files open with the site title as an H1, which Mintlify
requires of a custom `llms.txt` or `llms-full.txt` and which `LLMS Full
Valid` checks.
**Two caveats worth weighing at review.** This replaces a
Mintlify-maintained, auto-updating artifact with a generator in this
repo, so snippet-expansion correctness is now our problem. And the
expected range is still undocumented by both AFDocs and Mintlify — 6.2
MB is a large reduction, but whether it clears the bar is unverified
until this deploys.
### Correction to #5504
That PR's description claims the base64 removal roughly halved
`llms-full.txt`. **That was inferred, not measured, and it is wrong** —
the live file contains zero base64 and is unchanged in size, so Mintlify
never included data URIs there. The base64 work was still worth doing:
it cut the served `google_imagen` page from 6.69 MB to 7.6 KB, confirmed
live. I have not edited the merged PR body; say the word and I will.
### Markdown Content Parity — warning, no change recommended
Not a content defect. Regular pages differ by 0.7–3.9%, all UI chrome
("skip to main content", "was this page helpful"). The only substantial
gaps are OpenAPI pages, where the **markdown is richer than the HTML**
(175 words vs 55) because the html-only tokens are interactive
API-playground widgets with no markdown equivalent. Per the check's own
guidance, set `--parity-pass-threshold` and `--parity-warn-threshold` to
`0` for informational mode. Trimming markdown to match HTML would make
things worse for agents.
### Content Structure and Authentication — skipped, not failures
Both score 100. `Tabbed Content Serialization` is skipped because no
tabbed content was found in the sample; `Auth Alternative Access`
because all pages are public. Nothing to fix.
## Keeping it correct
Generating our own indexes removes the staleness problem outright. They
rebuild from source on every `make build`, `build/` is gitignored so
nothing can rot in the tree, `publish.yml` builds before deploying, and
new pages are picked up by walking the build tree with no registration
step. That is strictly fresher than what it replaces: Mintlify's
`llms-full.txt` was serving with `age: 25126` against a 24-hour cache
and did not contain pages that were already live.
What it does introduce is **correctness drift**, which nothing would
have noticed. Two guards for the two ways it can happen.
**Build-time validation** (`_validate_llms_indexes`) fails the build
when the emitted files break an invariant agents depend on:
- root `llms.txt` over 50,000 characters
- any section index over the same threshold
- a section index linking to further `.txt` files, which would push
those pages out of the coverage denominator
- a page listed in two indexes, or a page count that disagrees with what
was built
This runs wherever `make build` runs, so CI already covers it through
the link-checking job. Five unit tests exercise each failure mode — a
validator that cannot fail is not a guard.
**A weekly workflow** (`.github/workflows/check-llms-urls.yml`) covers
what no local check can. The API reference URLs are derived by
reproducing Mintlify's slug rules, which is not a published contract,
and a change on their side only surfaces against the deployed site.
`scripts/check_llms_urls.py` samples those URLs, reports any that stop
resolving, and names the two functions to fix. This is the guard that
would have caught the 47 fabricated URLs in #5504.
Worth noting: running the checker before shipping it caught a flaw in
the checker itself. Concurrent HEAD requests reported two healthy pages
as broken, so it now retries and falls back to GET. It is stable across
repeated runs at different seeds.
## Review notes
The one thing I could not verify locally is that Mintlify serves nested
`.txt` files from the build directory. Strong indirect evidence:
`/langsmith/langsmith-platform-openapi.json` returns 200 today, so
nested static assets are served, and nested `llms.txt` paths are not on
Mintlify's reserved-path list. **Worth confirming after deploy** that
`https://docs.langchain.com/oss/python/llms.txt` returns 200 — if it
does not, coverage would fall back to the root's 117 links and the split
should be reverted.
## AI disclosure
Authored with Claude Code (Claude Opus 5). Thresholds were read from the
AFDocs published check reference rather than assumed, and page
conservation was verified programmatically across all ten generated
files.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The sandbox auth proxy page never mentioned a proxy rule's `env_vars`,
and its GitHub example told readers to prefix every command with
`GH_TOKEN=dummy` — which is exactly what the field removes.
- Adds `env_vars` to the rule field table and a section covering what it
is for, the plaintext warning, and the precedence order (enabled rules <
the sandbox's own `env_vars` < variables managed by an enabled AWS or
GCP auth rule).
- Updates the GitHub example so the rule sets `GH_TOKEN`, and the `gh`
commands run unprefixed.
## Test Plan
- [x] `make lint_prose FILES=src/langsmith/sandbox-auth-proxy.mdx` — 0
errors, 0 warnings, 0 suggestions
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Context Hub repos can be mounted into sandboxes, but that was documented
nowhere — the sandbox SDK page covers files, service URLs, tunnels, and
retention, and never mentions mounts.
Adds a **Mount a Context Hub repo** section to `sandbox-sdk.mdx` with
Python and TypeScript examples, the mount-path rules, and the two sync
options (`initial_pull_only`, `read_only`). Links to it from the Context
Hub page's next steps.
⚠️ The `context_hub_mount` / `contextHubMount` helpers used in the
examples come from langchain-ai/langsmith-sdk#3351 — merge this after
the langsmith release that ships them.
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>