Patch release of the `langchain` package (langchain_v1): `1.3.16` →
`1.3.17`.
Bumps `version` in `pyproject.toml`, `__version__` in
`langchain/__init__.py`, and the corresponding entry in `uv.lock`.
---
*Prepared with the assistance of an AI agent.*
Human-provided HITL rejection reasons now retain enough context for the
model to understand that the tool was rejected rather than executed.
Previously, a custom `RejectDecision.message` replaced all rejection
framing, while the bare-rejection fallback instructed the model not to
retry. `HumanInTheLoopMiddleware` now uses minimal user-rejection
context in both cases without prescribing future model behavior.
## Release note
Human-in-the-loop rejection results now identify the user rejection
without instructing the model whether to retry.
_Developed with AI-agent assistance._
Made by [Open
SWE](https://openswe.vercel.app/agents/3df8f29d-d7d4-5296-9369-26755096058d)
---------
Co-authored-by: Harrison Chase <11986836+hwchase17@users.noreply.github.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Updates compatible minor and patch dependencies across `model-profiles`,
`standard-tests`, and `text-splitters`.
The `text-splitters` lockfile retains spaCy 3.8.13 because 3.8.15 cannot
install on Python 3.14, and retains ty 0.0.64 because 0.0.73 introduces
new diagnostics in existing NLTK and spaCy integrations. The compatible
pytest-socket, Ruff, NLTK, Transformers, and tiktoken updates remain
included.
Made by [Open
SWE](https://openswe.vercel.app/agents/0dfa86b3-bffc-58fa-b47a-d18f508e848f)
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: John Kennedy <65985482+jkennedyvz@users.noreply.github.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Add a repository-wide `<corridor>` block to `AGENTS.md` requiring agents
to plan first and run Corridor `analyzePlan` before generating or
modifying code.
This mirrors langchain-ai/deepagents#5740. The tags are explicit and
balanced so Corridor-specific guidance remains scoped.
---
Validation: `git diff --check` and a tag-balance assertion.
---------
Co-authored-by: langsmith-fleet[bot] <langsmith-fleet[bot]@users.noreply.github.com>
Bumps the minor-and-patch group with 1 update in the /libs/langchain
directory: python.
Updates `python` from 3.11-slim-bookworm to 3.14-slim-bookworm
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [types-toml](https://github.com/python/typeshed) from
0.10.8.20260408 to 0.10.8.20260518.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/python/typeshed/commits">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [types-pyyaml](https://github.com/python/typeshed) from
6.0.12.20260408 to 6.0.12.20260815.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/python/typeshed/commits">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [types-requests](https://github.com/python/typeshed) from
2.33.0.20260518 to 2.33.0.20260712.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/python/typeshed/commits">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Summary
- add monthly Dependabot coverage for the maintained
development-container Compose file
- add monthly Dependabot coverage for the LangChain development
Dockerfile
- keep major updates separate from minor and patch updates
## Test Plan
- parsed `.github/dependabot.yml` with Ruby `YAML.safe_load_file`
- ran `git diff --check`
- verified the staged diff contains only `.github/dependabot.yml`
This contribution was prepared with AI-agent assistance.
Co-authored-by: langsmith-fleet[bot] <langsmith-fleet[bot]@users.noreply.github.com>
Replace legacy sonar/sonar-pro examples in the ChatPerplexity docstring
with the Agent API pattern that 1.4.1 supports:
- use_responses_api=True with model_kwargs.preset + built-in web_search
tool
- explicit Agent API model IDs such as openai/gpt-5.6-sol
- pointer to the six current presets (fast, low, medium, high, xhigh,
wide-research) and to the Agent API models catalog
The rendered library docstring is what users see from IDEs and
help(ChatPerplexity), so this keeps it in sync with what 1.4.1 actually
routes to.
Fixes#39775
Closes#39568
Related: #33999
> [!WARNING]
> This PR expands the surface area for arbitrary code execution during
tool setup. Detecting injected arguments now requires calling
`typing.get_type_hints`, which *evaluates* string annotations (e.g.
those created by `from __future__ import annotations` or quoted forward
references) as Python expressions. As with existing type-hint resolution
paths, wrapped tool callables must be trusted application code — never
point `StructuredTool` at a callable whose module or annotations come
from an untrusted source.
Tools with a custom `args_schema` could drop injected arguments such as
`ToolRuntime` when the wrapped function's module uses postponed
annotations. The injected value was removed during input validation, so
an otherwise valid tool call failed at invocation.
---
`StructuredTool` now resolves annotations with
`typing.get_type_hints(..., include_extras=True)` before identifying
injected parameters. If an unrelated forward reference prevents
resolving the complete signature, each string annotation is resolved
independently so resolvable injected arguments are still preserved.
Callable wrappers resolve annotations from the source of their effective
signature, honoring `__wrapped__` and `__signature__`, while other
callable objects use their `__call__` method. `functools.partial`
callables retain their effective signature so already-bound injected
arguments remain excluded.
<details>
<summary><b>Before/after:</b> injected arg dropped under <code>from
__future__ import annotations</code></summary>
With postponed annotations, every annotation is stored as a plain
string. Previously `_injected_args_keys` read the raw `signature()`
annotations, so `runtime` was never recognized as injected and was
stripped during `args_schema` validation:
```python
from __future__ import annotations # all annotations become strings
from pydantic import BaseModel
from langchain_core.tools import tool, ToolRuntime
class InputSchema(BaseModel):
query: str
@tool(args_schema=InputSchema)
def my_tool(query: str, runtime: ToolRuntime) -> str:
"""Echo the query."""
return query
```
| | Behavior |
|---|---|
| **Before** | `runtime` not detected as injected → removed during
validation → tool call fails at invocation |
| **After** | `runtime` detected via `get_type_hints` → survives
validation and is injected at invocation; hidden from the model-facing
schema |
</details>
<details>
<summary><b>Before/after:</b> one unresolvable annotation disabling
injection for the whole signature</summary>
`get_type_hints` resolves *all* annotations at once and raises on the
first failure. A single unresolvable forward reference — even on an
unrelated parameter — previously meant *no* hints were available, so the
resolvable injected arg was dropped too:
```python
@tool(args_schema=InputSchema)
def my_tool(
query: "SomeTypeThatDoesNotExist", # unresolvable forward reference
runtime: "ToolRuntime", # resolvable injected arg
) -> str:
"""Echo the query."""
return query
```
| | Behavior |
|---|---|
| **Before** | `get_type_hints` raises on `query` → all hints discarded
→ `runtime` not detected as injected |
| **After** | each annotation is retried independently → `query` falls
back to its raw string (not injected), `runtime` still resolves and is
injected |
</details>
<details>
<summary><b>Before/after:</b> callable objects and wrappers</summary>
For non-function callables, the annotations now come from the source of
the *effective* signature: `__call__` for callable objects, and the
wrapped function for wrappers (`__wrapped__` / `__signature__`):
```python
class MyCallableTool:
def __call__(self, query: str, runtime: ToolRuntime) -> str:
return query
tool = StructuredTool.from_function(
func=MyCallableTool(),
name="my_tool",
description="Echo the query.",
args_schema=InputSchema,
)
```
| | Behavior |
|---|---|
| **Before** | annotations read from the wrong callable (or left as
unresolved strings) → `runtime` dropped |
| **After** | annotations resolved from `__call__` / the unwrapped
function → `runtime` injected correctly |
</details>
<details>
<summary><b>Unchanged:</b> <code>functools.partial</code> with an
already-bound injected arg</summary>
A `partial` that already binds an injected argument keeps its effective
signature — the bound parameter is absent, so nothing is re-injected
over it:
```python
from functools import partial
def fn(x: int, runtime: ToolRuntime, y: int) -> int:
return x + y
tool = StructuredTool.from_function(
func=partial(fn, 1, bound_runtime),
name="fn",
description="Add two numbers.",
args_schema=InputSchema,
)
```
**Before & after:** `runtime` is already bound by the `partial` →
excluded from the signature → the bound value is used as-is
</details>
Co-authored-by: Soban Shankar
<165470467+Soban-2004@users.noreply.github.com>
fixes#39099
We currently allow forward refs in pydantic v2 schemas upon creation:
```python
class Container(BaseModel):
rows: list["Row"] = [] # "Row" is declared below, after the tool is decorated
@tool
def my_tool(container: Container):
"""A tool whose schema depends on a forward reference that is not resolvable yet."""
return "ok"
class Row(BaseModel):
name: str
```
When it comes time to introspect the tool schema (notably in
`count_tokens_approximately` and `convert_to_openai_tool`), we rely on
[signature
introspection](https://github.com/langchain-ai/langchain/blob/943dd700ef7c33e3f1f21d3e280c9c249b88259c/libs/core/langchain_core/tools/base.py#L1654-L1661)
to extract the tool's input schema. If that contains invalid forward
references, there's no schema fields to extract which results in an
empty dict:
<details>
<summary>Invalid forward reference MRE</summary>
```python
from __future__ import annotations
import inspect
from pydantic import BaseModel, Field
from pydantic.errors import PydanticUndefinedAnnotation
from langchain_core.tools.base import get_all_basemodel_annotations
from langchain_core.utils.pydantic import _create_subset_model, model_json_schema
class Container(BaseModel):
"""A model with a nested forward reference that can never resolve."""
rows: list["UndefinedRow"] = Field(default_factory=list)
def main() -> None:
"""Print the field-selection inputs and their zero-field subset result."""
selected_annotations = get_all_basemodel_annotations(Container)
subset_schema = _create_subset_model(
"ContainerSubset",
Container,
list(selected_annotations),
fn_description=Container.__doc__,
)
print(f"Pydantic complete: {Container.__pydantic_complete__}")
print(f"Pydantic fields: {list(Container.model_fields)}")
print(f"inspect.signature: {inspect.signature(Container)}")
print(f"Fields selected by get_all_basemodel_annotations: {selected_annotations}")
print(f"Subset properties: {model_json_schema(subset_schema)['properties']}")
if __name__ == "__main__":
main()
```
```output
Pydantic complete: False
Pydantic fields: ['rows']
inspect.signature: (**data: 'Any') -> 'None'
Fields selected by get_all_basemodel_annotations: {}
Subset properties: {}
```
</details>
<details>
<summary>Valid forward reference MRE</summary>
```python
from __future__ import annotations
import inspect
from pydantic import BaseModel, Field
from pydantic.errors import PydanticUndefinedAnnotation
from langchain_core.tools.base import get_all_basemodel_annotations
from langchain_core.utils.pydantic import _create_subset_model, model_json_schema
class Container(BaseModel):
"""A model with a nested forward reference that can never resolve."""
rows: list["UndefinedRow"] = Field(default_factory=list)
class UndefinedRow(BaseModel):
name: str = Field()
def main() -> None:
"""Print the field-selection inputs and their zero-field subset result."""
Container.model_rebuild()
selected_annotations = get_all_basemodel_annotations(Container)
subset_schema = _create_subset_model(
"ContainerSubset",
Container,
list(selected_annotations),
fn_description=Container.__doc__,
)
print(f"Pydantic complete: {Container.__pydantic_complete__}")
print(f"Pydantic fields: {list(Container.model_fields)}")
print(f"inspect.signature: {inspect.signature(Container)}")
print(f"Fields selected by get_all_basemodel_annotations: {selected_annotations}")
print(f"Subset properties: {model_json_schema(subset_schema)['properties']}")
if __name__ == "__main__":
main()
```
```output
Pydantic complete: True
Pydantic fields: ['rows']
inspect.signature: (*, rows: list[__main__.UndefinedRow] = <factory>) -> None
Fields selected by get_all_basemodel_annotations: {'rows': list[__main__.UndefinedRow]}
Subset properties: {'rows': {'items': {'$ref': '#/$defs/UndefinedRow'}, 'title': 'Rows', 'type': 'array'}}
```
</details>
---
The fix is to
* at introspection time, resolve forward references using
`.model_rebuild()` that raises a pydantic exception if forward
references cant be resolved
* i'm also widening a pydantic utility to use a type guard instead of
having to use bool + cast
I'm intentionally not rebuilding pydantic v1 schemas in the same way
since
* forward references are specified by explicitly passing names into
`update_forward_refs`
* pydantic v1 is old news
Runnable snapshots currently embed the exact `langchain-core` version,
forcing unrelated snapshot rewrites during every release. Normalize only
the current `VERSION` to a stable placeholder before comparison, so
missing or stale version metadata still fails.
Made by [Open
SWE](https://openswe.vercel.app/agents/bfd72574-359e-544e-dbf5-78f8bae3636a)
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>