mirror of
https://github.com/hwchase17/langchain.git
synced 2026-06-09 18:50:33 +00:00
8fed1dd641f49b96d69ad3bfbb922ba6e45c2878
219 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
17d1c274c4 |
fix(langchain): improve HITL rejection guidance (#37859)
Improves the default `reject` `ToolMessage` so models see that the human denied the action, the tool was not executed, and the same call should not be retried unless the user asks. Also documents that clients can provide a custom `reject` message for domain-specific guidance. [Docs](https://github.com/langchain-ai/docs/pull/4269) _Opened collaboratively by Mason Daugherty and open-swe._ --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com> Co-authored-by: Mason Daugherty <61371264+mdrxy@users.noreply.github.com> Co-authored-by: Mason Daugherty <github@mdrxy.com> |
||
|
|
dfca7f4424 | feat(langchain): project subagent runs onto typed run.subagents channel (#37739) | ||
|
|
36be77b0f1 |
feat(langchain): add interrupt_mode and when predicate to HumanInTheLoopMiddleware (#37579)
Adds an optional `when` predicate to `InterruptOnConfig`, allowing
dynamic per-tool-call control over whether a HITL interrupt fires.
---
**`when` predicate in `InterruptOnConfig`**
```python
class InterruptOnConfig(TypedDict):
allowed_decisions: list[DecisionType]
description: NotRequired[str | _DescriptionFactory]
args_schema: NotRequired[dict[str, Any]]
when: NotRequired[Callable[[ToolCallRequest], bool]] # new
```
When provided, `when` is called before adding a tool call to the batch
interrupt. If it returns `False`, the call is auto-approved and
excluded. If it returns `True` (or `when` is absent), existing behaviour
is unchanged.
The predicate receives a `ToolCallRequest` with:
- `tool_call` — the raw tool call dict (name, args, id)
- `tool` — `None` (no `BaseTool` instance is available at the
`after_model` stage)
- `state` — current agent state
- `runtime` — a `ToolRuntime` constructed from the node-level `Runtime`,
with `tool_call_id` populated
Example:
```python
HumanInTheLoopMiddleware(
interrupt_on={
"delete_file": InterruptOnConfig(
allowed_decisions=["approve", "reject"],
when=lambda req: req.tool_call["args"].get("path", "").startswith("/etc"),
)
}
)
```
This change is fully backwards-compatible — `when` is `NotRequired` and
existing configs without it behave identically.
> This PR was developed with AI-agent assistance.
|
||
|
|
d08245f70d |
feat(langchain): redact streamed PII in flight on PIIMiddleware (#37616)
`PIIMiddleware` previously scrubbed detected PII only at the state level via its `after_model` / `before_model` hooks. Consumers reading the live stream — `astream_events(version="v3")` or `run.messages` / `run.tool_calls` / `run.values` — saw the raw model text, the raw tool-call args, the raw tool outputs, and the raw state snapshots until the run finished and the canonical conversation history was written. This change registers a stream transformer ahead of `MessagesTransformer` that redacts every wire surface of an agent run. The transformer holds a sliding lookback buffer (default 128 characters) per `(run_id, content-block index)` so PII patterns that straddle delta boundaries are caught before the safe prefix is released downstream. Anything older than the lookback is run through the configured detector and emitted; the trailing tail stays buffered until a later delta extends it past the cap or the block finishes. `_finalize_block` always re-runs detection over the full block snapshot so the finalized content lands fully redacted even when the in-flight buffer never released a tail (short responses, or PII arriving in the final delta). The `block` strategy is now supported on the streaming path via a buffering mode that withholds every delta until the block resolves — clean blocks release the full text at finalize, PII-bearing blocks zero the wire and let `after_model` / `apply_to_tool_results` raise `PIIDetectionError` on the original state message. Activation is gated on `apply_to_output=True`, matching the existing post-hoc semantics. The middleware's transformer factory is cloned by `StreamMux._make_child` into every subgraph scope, so attaching `PIIMiddleware` at the outer agent also redacts streamed deltas from sub-agents invoked inside tools. ## Tool-call and tools-channel coverage The transformer covers every wire surface of an agent run, not just AI message text: - **Streamed AI text deltas** (`content-block-delta` of type `text-delta`) — lookback machinery, redacted in place. - **Streamed tool-call args** (`content-block-delta` with `tool_call_chunk` / `server_tool_call_chunk` fields) — each delta carries the full cumulative args string; detection runs on the field directly and redacts in place. Verified empirically against `_compat_bridge.py` and the consumer-side `_merge_block_delta_into_store` snapshot-replace semantics. - **Finalized tool-call blocks** (`content-block-finish` with `tool_call` / `server_tool_call` / `invalid_tool_call`) — `args` dict walked recursively and each string leaf redacted. - **Tool execution events on the `tools` channel** — `tool-started.input`, `tool-output-delta`, `tool-finished.output`, `tool-error.message` all run through detection. String deltas use the same lookback machinery as text-deltas keyed by `tool_call_id`; structured payloads walk recursively. - **State snapshots on the `values` channel** — message lists are walked and each message's `.content` is redacted on a fresh copy. Graph state itself stays intact for the state-level enforcer (`apply_to_tool_results` via `before_model`) to act on independently. - **Legacy `(BaseMessage, metadata)` payloads** on the `messages` channel (Python 3.10 path, where `langgraph`'s `ASYNCIO_ACCEPTS_CONTEXT = sys.version_info >= (3, 11)` falls back to a code path that doesn't propagate the streaming callback into the chat model) — `.content` and `AIMessage.tool_calls[*].args` are scrubbed. For `block`, the event's `data` tuple is replaced with an empty-content copy so the original message stays in state for `after_model` to raise on. ## Worth a careful look - `_PIIStreamTransformer._mutate_text_delta` — lookback partition. Anything older than `lookback` characters is released after redaction; the tail stays buffered. Bulletproof against whitespace-permissive detectors (notably `credit_card`, whose regex matches across spaces). - `_PIIStreamTransformer._mutate_tool_call_chunk_delta` — direct in-place redaction of the cumulative args string. No buffer; the wire shape is cumulative-snapshot, the consumer-side merge is replace-not-append. - `_PIIStreamTransformer._mutate_legacy_payload` — the dual path: mutate-in-place for non-`block` (idempotent with `after_model`), replace-with-empty-copy for `block` (keeps original in graph state for `after_model` to raise on). - `_PIIStreamTransformer._redact_value` — the recursive walker. `BaseMessage` branch returns a fresh `.content`-redacted copy via `model_copy(update=...)` — never mutates in place — so tool-output payloads that wrap a `ToolMessage` and message lists in state snapshots flow through cleanly. - The new `transformers` attribute on `PIIMiddleware`: this is what makes `create_agent` pick the factory up. Multiple `PIIMiddleware` instances each register one transformer; ordering is preserved within the `before_builtins` lane. ## Compatibility Bumps `langgraph` to `>=1.2.1` for the `before_builtins` opt-in on `StreamTransformer`. |
||
|
|
1aa4496fb4 |
feat(langchain): register stream transformers on middleware (#37591)
Adds a `transformers` attribute to `AgentMiddleware` so middleware can
declare scope-aware `StreamTransformer` factories alongside their
`tools` and lifecycle hooks. `create_agent` merges middleware-registered
factories with any caller-supplied ones at compile time.
## API
```python
class MyMiddleware(AgentMiddleware):
transformers = (MyTransformer,) # factory: (scope,) -> StreamTransformer
```
When the agent compiles, the final transformer order on the run mux is:
1. Built-in ``ToolCallTransformer``
2. Middleware-registered factories, in middleware order
3. Caller-supplied ``transformers=`` from ``create_agent``
This ordering keeps the built-in tool-call projection in front of any
consumer transformers and gives caller-supplied entries the final word.
|
||
|
|
d328d84b78 |
fix(langchain): sort glob_search results by mtime (newest first) (#37462)
Closes #37369 --- The `glob_search` tool in `FilesystemFileSearchMiddleware` documents that results are "sorted by modification time (most recently modified first)", but the implementation was returning files in the arbitrary order provided by `Path.glob()`. This change adds a sort by modification timestamp (`modified_at`), in descending order, immediately before extracting the file paths for the return value. No public API changes. --------- Co-authored-by: Mason Daugherty <github@mdrxy.com> |
||
|
|
36c381b149 | fix(langchain): alias Bedrock providers in summarization token check (#37453) | ||
|
|
da380bccf8 | chore(infra): merge v1.4 into master (#37350) | ||
|
|
9c48a120b9 | revert: feat(langchain): ls_agent_type tag on create_agent calls (#37249) | ||
|
|
ba56ac6f03 |
feat(langchain): add respond decision to HITL middleware (#37095)
Extends `HumanInTheLoopMiddleware` with a new `respond` decision type
for "ask user" style tools — tools whose real implementation is the
human's response. The interrupt is raised with the tool call as usual;
the resume payload becomes the body of a synthetic `ToolMessage` with
`status="success"`, and the tool itself is not executed.
This complements `reject` (which produces a synthetic `ToolMessage` with
`status="error"`) by enabling the symmetric success path: a reviewer can
answer on the tool's behalf without invoking it.
## Changes
- New `RespondDecision` `TypedDict` with a required `message: str`
field; added to the `Decision` union.
- `"respond"` added to the `DecisionType` literal.
- `_process_decision` handles `"respond"` by emitting a `ToolMessage`
with `status="success"` and preserving the original tool call on the
`AIMessage` so provider-required tool-call/tool-message pairing is
maintained.
- The `True` shortcut in `interrupt_on` now expands to `["approve",
"edit", "reject", "respond"]`, so existing callers that opted into "all
decisions" pick up the new capability without code changes. The `reject`
decision already permits a reviewer to inject arbitrary `ToolMessage`
content, so `respond` extends the same trust model — not a new
capability class.
## Example
```python
from langchain.agents.middleware import HumanInTheLoopMiddleware
middleware = HumanInTheLoopMiddleware(
interrupt_on={"ask_user": {"allowed_decisions": ["respond"]}}
)
# Resume payload: {"decisions": [{"type": "respond", "message": "blue"}]}
# → synthetic ToolMessage(content="blue", status="success") for `ask_user`.
```
---
*Implementation drafted with AI-agent assistance.*
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
ee95ad6907 |
feat(langchain): ls_agent_type tag on create_agent calls (#36774)
|
||
|
|
c87cd04927 |
release(core): release 1.3.0 (#36851)
xRelease 1.3.0 |
||
|
|
8f5c800f41 |
test(langchain): test cache hit/miss on lru (#36659)
add quick test |
||
|
|
1ca47a5411 |
perf(langchain): add another init test with middleware (#36644)
add another init test with middleware |
||
|
|
af4d711a2f |
chore(core): reduce streaming metadata / perf (#36588)
- looking into reducing streaming metadata / perfm --------- Co-authored-by: William Fu-Hinthorn <13333726+hinthornw@users.noreply.github.com> |
||
|
|
f0c5a28fa0 |
perf(langchain): add benchmark command (#36641)
add benchmark in Makefile |
||
|
|
4e55c555ad |
test(langchain): cover runtime recursion limit override in create_agent (#36376)
Extends the existing unit test for to verify that a per-invoke override is visible inside the tool runtime config. This keeps the coverage in the existing fake-model end-to-end test and exercises both the default config path and the override path in one place. Created with [Deep Agents CLI](https://docs.langchain.com/oss/python/deepagents/cli/overview) using gpt-5.4 (provider: openai). |
||
|
|
c7a677bba5 |
chore(langchain): add async implementation to todolist and test (#36313)
add async func as well |
||
|
|
2f64d80cc6 |
fix(core,model-profiles): add missing ModelProfile fields, warn on schema drift (#36129)
PR #35788 added 7 new fields to the `langchain-profiles` CLI output (`name`, `status`, `release_date`, `last_updated`, `open_weights`, `attachment`, `temperature`) but didn't update `ModelProfile` in `langchain-core`. Partner packages like `langchain-aws` that set `extra="forbid"` on their Pydantic models hit `extra_forbidden` validation errors when Pydantic encountered undeclared TypedDict keys at construction time. This adds the missing fields, makes `ModelProfile` forward-compatible, provides a base-class hook so partners can stop duplicating model-profile validator boilerplate, migrates all in-repo partners to the new hook, and adds runtime + CI-time warnings for schema drift. ## Changes ### `langchain-core` - Add `__pydantic_config__ = ConfigDict(extra="allow")` to `ModelProfile` so unknown profile keys pass Pydantic validation even on models with `extra="forbid"` — forward-compatibility for when the CLI schema evolves ahead of core - Declare the 7 missing fields on `ModelProfile`: `name`, `status`, `release_date`, `last_updated`, `open_weights` (metadata) and `attachment`, `temperature` (capabilities) - Add `_warn_unknown_profile_keys()` in `model_profile.py` — emits a `UserWarning` when a profile dict contains keys not in `ModelProfile`, suggesting a core upgrade. Wrapped in a bare `except` so introspection failures never crash model construction - Add `BaseChatModel._resolve_model_profile()` hook that returns `None` by default. Partners can override this single method instead of redefining the full `_set_model_profile` validator — the base validator calls it automatically - Add `BaseChatModel._check_profile_keys` as a separate `model_validator` that calls `_warn_unknown_profile_keys`. Uses a distinct method name so partner overrides of `_set_model_profile` don't inadvertently suppress the check ### `langchain-profiles` CLI - Add `_warn_undeclared_profile_keys()` to the CLI (`cli.py`), called after merging augmentations in `refresh()` — warns at profile-generation time (not just runtime) when emitted keys aren't declared in `ModelProfile`. Gracefully skips if `langchain-core` isn't installed - Add guard test `test_model_data_to_profile_keys_subset_of_model_profile` in model-profiles — feeds a fully-populated model dict to `_model_data_to_profile()` and asserts every emitted key exists in `ModelProfile.__annotations__`. CI fails before any release if someone adds a CLI field without updating the TypedDict ### Partner packages - Migrate all 10 in-repo partners to the `_resolve_model_profile()` hook, replacing duplicated `@model_validator` / `_set_model_profile` overrides: anthropic, deepseek, fireworks, groq, huggingface, mistralai, openai (base + azure), openrouter, perplexity, xai - Anthropic retains custom logic (context-1m beta → `max_input_tokens` override); all others reduce to a one-liner - Add `pr_lint.yml` scope for the new `model-profiles` package |
||
|
|
9e4a6013be | fix(openai): add type: message to Responses API input items (#35693) | ||
|
|
46fdade7e6 | fix(langchain): normalize custom detector output to prevent KeyError in hash/mask strategies (#35651) | ||
|
|
bed4d2686a | chore(langchain): switch refs from gemini-3 to gemini-3.1 (#35535) | ||
|
|
83f81d65af |
fix(langchain): allow Gemini 3 models to use ProviderStrategy with tools (#34464)
|
||
|
|
8f1bc0d3ae | feat(openai): support automatic server-side compaction (#35212) | ||
|
|
65c224a3da |
chore(core): raise more descriptive model error in init_chat_model (#35167)
|
||
|
|
de05838fca |
chore(deps): bump the langchain-deps group across 3 directories with 40 updates (#35129)
Bumps the langchain-deps group with 10 updates in the /libs/core directory: | Package | From | To | | --- | --- | --- | | [langsmith](https://github.com/langchain-ai/langsmith-sdk) | `0.6.0` | `0.7.1` | | [tenacity](https://github.com/jd/tenacity) | `9.1.2` | `9.1.4` | | [packaging](https://github.com/pypa/packaging) | `25.0` | `26.0` | | [uuid-utils](https://github.com/aminalaee/uuid-utils) | `0.12.0` | `0.14.0` | | [ruff](https://github.com/astral-sh/ruff) | `0.14.11` | `0.15.0` | | [types-requests](https://github.com/typeshed-internal/stub_uploader) | `2.31.0.6` | `2.32.4.20260107` | | [setuptools](https://github.com/pypa/setuptools) | `78.1.1` | `82.0.0` | | [pytest](https://github.com/pytest-dev/pytest) | `8.4.2` | `9.0.2` | | [pytest-watcher](https://github.com/olzhasar/pytest-watcher) | `0.4.3` | `0.6.3` | | [pytest-codspeed](https://github.com/CodSpeedHQ/pytest-codspeed) | `4.2.0` | `4.3.0` | Bumps the langchain-deps group with 31 updates in the /libs/langchain directory: | Package | From | To | | --- | --- | --- | | [langsmith](https://github.com/langchain-ai/langsmith-sdk) | `0.4.31` | `0.7.1` | | [packaging](https://github.com/pypa/packaging) | `25.0` | `26.0` | | [pydantic](https://github.com/pydantic/pydantic) | `2.12.1` | `2.12.5` | | [ruff](https://github.com/astral-sh/ruff) | `0.14.11` | `0.15.0` | | [types-requests](https://github.com/typeshed-internal/stub_uploader) | `2.31.0.6` | `2.32.4.20260107` | | [setuptools](https://github.com/pypa/setuptools) | `80.9.0` | `82.0.0` | | [pytest](https://github.com/pytest-dev/pytest) | `8.4.2` | `9.0.2` | | [pytest-watcher](https://github.com/olzhasar/pytest-watcher) | `0.4.3` | `0.6.3` | | [numpy](https://github.com/numpy/numpy) | `1.26.4` | `2.2.6` | | [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) | `2.0.43` | `2.0.46` | | [langchain-anthropic](https://github.com/langchain-ai/langchain) | `0.0.1.post1` | `1.3.2` | | [langchain-google-vertexai](https://github.com/langchain-ai/langchain-google) | `2.1.2` | `3.2.2` | | [langchain-google-genai](https://github.com/langchain-ai/langchain-google) | `2.1.12` | `4.2.0` | | [langchain-fireworks](https://github.com/langchain-ai/langchain) | `1.0.0a1` | `1.1.0` | | [langchain-ollama](https://github.com/langchain-ai/langchain) | `1.0.0a1` | `1.0.1` | | [langchain-mistralai](https://github.com/langchain-ai/langchain) | `0.2.12` | `1.1.1` | | [langchain-huggingface](https://github.com/langchain-ai/langchain) | `1.0.0` | `1.2.0` | | [langchain-groq](https://github.com/langchain-ai/langchain) | `1.0.0a1` | `1.1.2` | | [langchain-aws](https://github.com/langchain-ai/langchain-aws) | `1.0.0a1` | `1.2.3` | | [langchain-deepseek](https://github.com/langchain-ai/langchain) | `1.0.0a1` | `1.0.1` | | [langchain-xai](https://github.com/langchain-ai/langchain) | `1.0.0a1` | `1.2.2` | | [langchain-perplexity](https://github.com/langchain-ai/langchain) | `1.0.0a1` | `1.1.0` | | [pytest-cov](https://github.com/pytest-dev/pytest-cov) | `4.1.0` | `7.0.0` | | [lark](https://github.com/lark-parser/lark) | `1.3.0` | `1.3.1` | | [wrapt](https://github.com/GrahamDumpleton/wrapt) | `1.17.3` | `2.1.1` | | [python-dotenv](https://github.com/theskumar/python-dotenv) | `1.1.1` | `1.2.1` | | langchainhub | `0.1.18` | `0.1.21` | | [mypy-protobuf](https://github.com/nipunn1313/mypy-protobuf) | `3.6.0` | `5.0.0` | | [types-pytz](https://github.com/typeshed-internal/stub_uploader) | `2023.4.0.20240130` | `2025.2.0.20251108` | | [fastapi](https://github.com/fastapi/fastapi) | `0.128.0` | `0.128.6` | | [playwright](https://github.com/microsoft/playwright-python) | `1.55.0` | `1.58.0` | Bumps the langchain-deps group with 17 updates in the /libs/langchain_v1 directory: | Package | From | To | | --- | --- | --- | | [pydantic](https://github.com/pydantic/pydantic) | `2.12.4` | `2.12.5` | | [ruff](https://github.com/astral-sh/ruff) | `0.14.11` | `0.15.0` | | [pytest](https://github.com/pytest-dev/pytest) | `8.4.2` | `9.0.2` | | [pytest-watcher](https://github.com/olzhasar/pytest-watcher) | `0.4.3` | `0.6.3` | | [langchain-google-vertexai](https://github.com/langchain-ai/langchain-google) | `3.0.3` | `3.2.2` | | [langchain-google-genai](https://github.com/langchain-ai/langchain-google) | `3.0.3` | `4.2.0` | | [langchain-fireworks](https://github.com/langchain-ai/langchain) | `1.0.0` | `1.1.0` | | [langchain-ollama](https://github.com/langchain-ai/langchain) | `1.0.0` | `1.0.1` | | [langchain-mistralai](https://github.com/langchain-ai/langchain) | `1.0.1` | `1.1.1` | | [langchain-huggingface](https://github.com/langchain-ai/langchain) | `1.0.1` | `1.2.0` | | [langchain-groq](https://github.com/langchain-ai/langchain) | `1.0.1` | `1.1.2` | | [langchain-aws](https://github.com/langchain-ai/langchain-aws) | `1.0.0` | `1.2.3` | | [langchain-xai](https://github.com/langchain-ai/langchain) | `1.0.0` | `1.2.2` | | [langchain-perplexity](https://github.com/langchain-ai/langchain) | `1.0.0` | `1.1.0` | | [wrapt](https://github.com/GrahamDumpleton/wrapt) | `1.17.3` | `2.1.1` | | [langgraph](https://github.com/langchain-ai/langgraph) | `1.0.7` | `1.0.8` | | [langchain-azure-ai](https://github.com/langchain-ai/langchain-azure) | `1.0.3` | `1.0.4` | Updates `langsmith` from 0.6.0 to 0.7.1 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's releases</a>.</em></p> <blockquote> <h2>v0.7.1</h2> <h2>What's Changed</h2> <ul> <li>release(js): 0.5.0 by <a href="https://github.com/jacoblee93"><code>@jacoblee93</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2381">langchain-ai/langsmith-sdk#2381</a></li> <li>fix(ci): Convert JS release workflow to use OIDC by <a href="https://github.com/jacoblee93"><code>@jacoblee93</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2383">langchain-ai/langsmith-sdk#2383</a></li> <li>feat(js): run evaluations as examples complete by <a href="https://github.com/bees"><code>@bees</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2349">langchain-ai/langsmith-sdk#2349</a></li> <li>fix(js): Restore default concurrency behavior for JS eval runner by <a href="https://github.com/jacoblee93"><code>@jacoblee93</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2385">langchain-ai/langsmith-sdk#2385</a></li> <li>fix(js,py): Adds backcompat for old prompt cache API by <a href="https://github.com/jacoblee93"><code>@jacoblee93</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2386">langchain-ai/langsmith-sdk#2386</a></li> <li>fix(js): Add system message visibility to Anthropic wrapper traces by <a href="https://github.com/veeceey"><code>@veeceey</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2375">langchain-ai/langsmith-sdk#2375</a></li> <li>release(py): 0.7.1 by <a href="https://github.com/jacoblee93"><code>@jacoblee93</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2388">langchain-ai/langsmith-sdk#2388</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/veeceey"><code>@veeceey</code></a> made their first contribution in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2375">langchain-ai/langsmith-sdk#2375</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.0...v0.7.1">https://github.com/langchain-ai/langsmith-sdk/compare/v0.7.0...v0.7.1</a></p> <h2>v0.7.0</h2> <h2>What's Changed</h2> <ul> <li>chore(deps): bump form-data from 4.0.3 to 4.0.5 in /js by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2282">langchain-ai/langsmith-sdk#2282</a></li> <li>chore(deps-dev): bump langchain from 0.3.29 to 0.3.37 in /js by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2283">langchain-ai/langsmith-sdk#2283</a></li> <li>chore(deps): bump vite from 6.3.5 to 6.4.1 in /js by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2284">langchain-ai/langsmith-sdk#2284</a></li> <li>chore:deps by <a href="https://github.com/jkennedyvz"><code>@jkennedyvz</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2356">langchain-ai/langsmith-sdk#2356</a></li> <li>chore(deps): bump python-multipart from 0.0.21 to 0.0.22 in /python by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2351">langchain-ai/langsmith-sdk#2351</a></li> <li>chore(deps): bump protobuf from 6.33.2 to 6.33.5 in /python by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2352">langchain-ai/langsmith-sdk#2352</a></li> <li>chore(deps): bump pyasn1 from 0.6.1 to 0.6.2 in /python by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2353">langchain-ai/langsmith-sdk#2353</a></li> <li>chore(deps-dev): bump esbuild from 0.20.2 to 0.25.0 in /js/internal/environment_tests/test-exports-esbuild in the npm_and_yarn group across 1 directory by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2354">langchain-ai/langsmith-sdk#2354</a></li> <li>chore(deps): bump jws from 4.0.0 to 4.0.1 in /js by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2355">langchain-ai/langsmith-sdk#2355</a></li> <li>chore(deps): bump actions/setup-node from 3 to 6 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2357">langchain-ai/langsmith-sdk#2357</a></li> <li>chore(deps): bump actions/github-script from 6 to 8 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2358">langchain-ai/langsmith-sdk#2358</a></li> <li>chore(deps): bump actions/checkout from 2 to 6 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2359">langchain-ai/langsmith-sdk#2359</a></li> <li>chore(deps): bump astral-sh/setup-uv from 6 to 7 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2360">langchain-ai/langsmith-sdk#2360</a></li> <li>chore(deps): bump actions/cache from 4 to 5 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2361">langchain-ai/langsmith-sdk#2361</a></li> <li>chore(deps-dev): bump ty from 0.0.10 to 0.0.15 in /python in the py-minor-and-patch group by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2362">langchain-ai/langsmith-sdk#2362</a></li> <li>chore(deps): update httpx requirement from <0.28.0,>=0.23.0 to >=0.23.0,<0.29.0 in /python by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2363">langchain-ai/langsmith-sdk#2363</a></li> <li>chore(deps-dev): bump eslint-plugin-prettier from 4.2.1 to 4.2.5 in /js by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2366">langchain-ai/langsmith-sdk#2366</a></li> <li>chore(deps-dev): bump babel-jest from 29.7.0 to 30.2.0 in /js by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2365">langchain-ai/langsmith-sdk#2365</a></li> <li>chore: adding workflow perms by <a href="https://github.com/jkennedyvz"><code>@jkennedyvz</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2367">langchain-ai/langsmith-sdk#2367</a></li> <li>fix: esbuild CVE dependabot 78 by <a href="https://github.com/jkennedyvz"><code>@jkennedyvz</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2369">langchain-ai/langsmith-sdk#2369</a></li> <li>fix(deps): resolve glob CLI command injection (CVE-2025-64756) by <a href="https://github.com/jkennedyvz"><code>@jkennedyvz</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2368">langchain-ai/langsmith-sdk#2368</a></li> <li>fix(deps): resolve js-yaml prototype pollution vulnerability by <a href="https://github.com/jkennedyvz"><code>@jkennedyvz</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2370">langchain-ai/langsmith-sdk#2370</a></li> <li>chore(deps): bump the js-minor-and-patch group in /js with 20 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2364">langchain-ai/langsmith-sdk#2364</a></li> <li>chore: rm unused fn by <a href="https://github.com/hinthornw"><code>@hinthornw</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2346">langchain-ai/langsmith-sdk#2346</a></li> <li>feat(py,js): Use global singleton for prompt cache, enable by default by <a href="https://github.com/jacoblee93"><code>@jacoblee93</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2371">langchain-ai/langsmith-sdk#2371</a></li> <li>chore: Support jwt auth in replicas by <a href="https://github.com/hinthornw"><code>@hinthornw</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2293">langchain-ai/langsmith-sdk#2293</a></li> <li>release(py): 0.7.0 by <a href="https://github.com/jacoblee93"><code>@jacoblee93</code></a> in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2382">langchain-ai/langsmith-sdk#2382</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/jkennedyvz"><code>@jkennedyvz</code></a> made their first contribution in <a href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2356">langchain-ai/langsmith-sdk#2356</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.6.9...v0.7.0">https://github.com/langchain-ai/langsmith-sdk/compare/v0.6.9...v0.7.0</a></p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
0040e1a8aa | fix(langchain): fix token counting on partial message sequences (#35101) | ||
|
|
ce5f73e07c |
refactor(langchain): rename _SUPPORTED_PROVIDERS -> _BUILTIN_PROVIDERS (#35100)
|
||
|
|
8767a462ca |
feat: support state updates from wrap_model_call with command(s) (#35033)
Alternative to https://github.com/langchain-ai/langchain/pull/35024. Paving the way for summarization in `wrap_model_call` (which requires state updates). --- Add `ExtendedModelResponse` dataclass that allows `wrap_model_call` middleware to return a `Command` alongside the model response for additional state updates. ```py @dataclass class ExtendedModelResponse(Generic[ResponseT]): model_response: ModelResponse[ResponseT] command: Command ``` ## Motivation Previously, `wrap_model_call` middleware could only return a `ModelResponse` or `AIMessage` — there was no way to inject additional state updates (e.g. custom state fields) from the model call middleware layer. `ExtendedModelResponse` fills this gap by accepting an optional `Command`. This feature is needed by the summarization middleware, which needs to track summarization trigger points calculated during `wrap_model_call`. ## Why `Command` instead of a plain `state_update` dict? We chose `Command` rather than the raw `state_update: dict` approach from the earlier iteration because `Command` is the established LangGraph primitive for state updates from nodes. Using `Command` means: - State updates flow through the graph's reducers (e.g. `add_messages`) rather than being merged as raw dicts. This makes messages updates additive alongside the model response instead of replacing them. - Consistency with `wrap_tool_call`, which already returns `Command`. - Future-proof: as `Command` gains new capabilities (e.g. `goto`, `send`), middleware can leverage them without API changes. ## Why keep `model_response` separate instead of using `Command` directly? The model node needs to distinguish the model's actual response (messages + structured output) from supplementary middleware state updates. If middleware returned only a `Command`, there would be no clean way to extract the `ModelResponse` for structured output handling, response validation, and the core model-to-tools routing logic. Keeping `model_response` explicit preserves a clear boundary between "what the model said" and "what middleware wants to update." Also, in order to avoid breaking, the `handler` passed to `wrap_tool_call` needs to always return a `ModelResponse`. There's no easy way to preserve this if we pump it into a `Command`. One nice thing about having this `ExtendedModelResponse` structure is that it's extensible if we want to add more metadata in the future. ## Composition When multiple middleware layers return `ExtendedModelResponse`, their commands compose naturally: - **Inner commands propagate outward:** At composition boundaries, `ExtendedModelResponse` is unwrapped to its underlying `ModelResponse` so outer middleware always sees a plain `ModelResponse` from `handler()`. The inner command is captured and accumulated. - **Commands are applied through reducers:** Each `Command` becomes a separate state update applied through the graph's reducers. For messages, this means they're additive (via `add_messages`), not replacing. - **Outer wins on conflicts:** For non-reducer state fields, commands are applied inner-first then outer, so the outermost middleware's value takes precedence on conflicting keys. - **Retry-safe:** When outer middleware retries by calling `handler()` again, accumulated inner commands are cleared and re-collected from the fresh call. ```python class Outer(AgentMiddleware): def wrap_model_call(self, request, handler): response = handler(request) # sees ModelResponse, not ExtendedModelResponse return ExtendedModelResponse( model_response=response, command=Command(update={"outer_key": "val"}), ) class Inner(AgentMiddleware): def wrap_model_call(self, request, handler): response = handler(request) return ExtendedModelResponse( model_response=response, command=Command(update={"inner_key": "val"}), ) # Final state merges both commands: {"inner_key": "val", "outer_key": "val"} ``` ## Backwards compatibility Fully backwards compatible. The `ModelCallResult` type alias is widened from `ModelResponse | AIMessage` to `ModelResponse | AIMessage | ExtendedModelResponse`, but existing middleware returning `ModelResponse` or `AIMessage` continues to work identically. ## Internals - `model_node` / `amodel_node` now return `list[Command]` instead of `dict[str, Any]` - `_build_commands` converts the model response + accumulated middleware commands into a list of `Command` objects for LangGraph - `_ComposedExtendedModelResponse` is the internal type that accumulates commands across layers during composition |
||
|
|
d181a59ebe |
test(langchain): types in test_tool_call_limit and test_model_retry (#34629)
Co-authored-by: Mason Daugherty <github@mdrxy.com> Co-authored-by: Mason Daugherty <mason@langchain.dev> |
||
|
|
dde2012b83 |
feat: threading context through create_agent flows + middleware (#34978)
Closes https://github.com/langchain-ai/langchain/issues/33956 * Making `ModelRequest` generic on `ContextT` and `ResponseT` so that we can thread type information through to `wrap_model_call` * Making builtin middlewares generic on `ContextT` and `ResponseT` so their context and response types can be inferred from the `create_agent` signature See new tests that verify backwards compatibility (for cases where folks use custom middleware that wasn't parametrized). This fixes: 1. Lack of access to context and response types in `wrap_model_call` 2. Lack of cohesion between middleware context + response types with those specified in `create_agent` See examples below: ### Type-safe context and response access ```python class MyMiddleware(AgentMiddleware[AgentState[AnalysisResult], UserContext, AnalysisResult]): def wrap_model_call( self, request: ModelRequest[UserContext], handler: Callable[[ModelRequest[UserContext]], ModelResponse[AnalysisResult]], ) -> ModelResponse[AnalysisResult]: # ✅ Now type-safe: IDE knows user_id exists and is str user_id: str = request.runtime.context["user_id"] # ❌ mypy error: "session_id" doesn't exist on UserContext request.runtime.context["session_id"] response = handler(request) if response.structured_response is not None: # ✅ Now type-safe: IDE knows sentiment exists and is str sentiment: str = response.structured_response.sentiment # ❌ mypy error: "summary" doesn't exist on AnalysisResult response.structured_response.summary return response ``` ### Mismatched middleware/schema caught at `create_agent` ```python class SessionMiddleware(AgentMiddleware[AgentState[Any], SessionContext, Any]): ... # ❌ mypy error: SessionMiddleware expects SessionContext, not UserContext create_agent( model=model, middleware=[SessionMiddleware()], context_schema=UserContext, # mismatch! ) class AnalysisMiddleware(AgentMiddleware[AgentState[AnalysisResult], ContextT, AnalysisResult]): ... # ❌ mypy error: AnalysisMiddleware expects AnalysisResult, not SummaryResult create_agent( model=model, middleware=[AnalysisMiddleware()], response_format=SummaryResult, # mismatch! ) ``` |
||
|
|
91bb474dbc | fix(langchain): avoid UnboundLocalError when no AIMessage exists (#34816) | ||
|
|
d189663b47 | fix: reuse ToolStrategy in agent factory to prevent name mismatch (#34871) | ||
|
|
72333ad644 |
fix(langchain): blocking unit test (#34866)
= |
||
|
|
ca9d2c0bdd | test(langchain): use blockbuster to detect blocking calls in the async event loop (#34777) | ||
|
|
bc8620189c |
feat: dynamic tool registration via middleware (#34842)
dependent upon https://github.com/langchain-ai/langgraph/pull/6711 1. relax constraint in `factory.py` to allow for tools not pre-registered in the `ModelRequest.tools` list 2. always add tool node if `wrap_tool_call` or `awrap_tool_call` is implemented 3. add tests confirming you can register new tools at runtime in `wrap_model_call` and execute them via `wrap_tool_call` allows for the following pattern ```py from langchain_core.messages import HumanMessage, ToolMessage from langchain_core.tools import tool from libs.langchain_v1.langchain.agents.factory import create_agent from libs.langchain_v1.langchain.agents.middleware.types import ( AgentMiddleware, ModelRequest, ToolCallRequest, ) @tool def get_weather(location: str) -> str: """Get the current weather for a location.""" return f"The weather in {location} is sunny and 72°F." @tool def calculate_tip(bill_amount: float, tip_percentage: float = 20.0) -> str: """Calculate the tip amount for a bill.""" tip = bill_amount * (tip_percentage / 100) return f"Tip: ${tip:.2f}, Total: ${bill_amount + tip:.2f}" class DynamicToolMiddleware(AgentMiddleware): """Middleware that adds and handles a dynamic tool.""" def wrap_model_call(self, request: ModelRequest, handler): updated = request.override(tools=[*request.tools, calculate_tip]) return handler(updated) def wrap_tool_call(self, request: ToolCallRequest, handler): if request.tool_call["name"] == "calculate_tip": return handler(request.override(tool=calculate_tip)) return handler(request) agent = create_agent(model="openai:gpt-4o-mini", tools=[get_weather], middleware=[DynamicToolMiddleware()]) result = agent.invoke({ "messages": [HumanMessage("What's the weather in NYC? Also calculate a 20% tip on a $85 bill")] }) for msg in result["messages"]: msg.pretty_print() ``` |
||
|
|
09c3c52fd0 |
fix(langchain): add metadata configuration to summarization model invocation (#34763)
We need to set `{"metadata": {"lc_source": "summarization"}}` on the
invocation so that consumers (e.g. `deepagents-cli`) can see that a
summarization LLM call is being made, and therefore take any necessary
actions (such as updating the status line to say `'Currently
summarizing...'`
See https://github.com/langchain-ai/deepagents/pull/742 for more
Related to #34693 (but for outbound)
|
||
|
|
73ebaddcf0 | chore: add tests for agent name metadata when streaming (#34764) | ||
|
|
ee6fce5586 |
Revert "metadata"
This reverts commit
|
||
|
|
13301a779e | metadata | ||
|
|
5799aa1045 |
chore: add tests for private state attr use (really, lack thereof) (#34725)
If a user injects a private state value, it should be ignored (and is)! |
||
|
|
725d204b95 | fix(langchain): tag messages generated from summarization (#34693) | ||
|
|
a7b943bbe3 |
fix(langchain): activate test_return_direct_spec tests, fix types (#34565)
Co-authored-by: Mason Daugherty <mason@langchain.dev> Co-authored-by: Mason Daugherty <github@mdrxy.com> |
||
|
|
5fbf270c9d |
chore(langchain): fix types in test_todo, test_tool_retry (#34503)
Co-authored-by: Mason Daugherty <mason@langchain.dev> |
||
|
|
e73b027686 |
chore(langchain): fix types in test_shell_tool (#34502)
Co-authored-by: Mason Daugherty <mason@langchain.dev> |
||
|
|
ecd19ff71f |
chore(langchain): activate mypy warn_return_any rule (#34549)
Co-authored-by: Mason Daugherty <github@mdrxy.com> Co-authored-by: Mason Daugherty <mason@langchain.dev> |
||
|
|
cb0d227d8a |
chore(langchain): fix types in test_tool_selection and test_tool_emulator (#34499)
Co-authored-by: Mason Daugherty <mason@langchain.dev> |
||
|
|
b688e36e38 |
chore(langchain): fix types in test_shell_execution_policies (#34498)
Co-authored-by: Mason Daugherty <mason@langchain.dev> |
||
|
|
36e590ca5f |
test(langchain): complete and activate test_responses tests (#34560)
Co-authored-by: Mason Daugherty <github@mdrxy.com> Co-authored-by: Mason Daugherty <mason@langchain.dev> |
||
|
|
fc417aaf17 |
fix(langchain): activate mypy warn-unreachable (#34553)
Co-authored-by: Mason Daugherty <mason@langchain.dev> |