Adds @overload signatures to `Runnable.astream_events` and introduces a
new `Runnable.stream_events` sync method, both accepting `version='v3'`.
The base-class implementation raises `NotImplementedError` with a message
directing callers to use a subclass that implements the v3 streaming
protocol (BaseChatModel, CompiledGraph). v1/v2 behavior is unchanged.
## Description
This PR adds a new `PerplexityEmbeddings` class to the
`langchain-perplexity` partner package, providing first-class support
for the Perplexity Embeddings API alongside the existing
`ChatPerplexity`, `PerplexitySearchRetriever`, and
`PerplexitySearchResults` integrations.
### What was added
- `langchain_perplexity/embeddings.py` — `PerplexityEmbeddings` class
implementing `langchain_core.embeddings.Embeddings` with sync
(`embed_documents`, `embed_query`) and async (`aembed_documents`,
`aembed_query`) methods. Defaults to model `pplx-embed-v1-4b` and reuses
the existing `_utils.initialize_client` helper for API key resolution
(`PPLX_API_KEY` / `PERPLEXITY_API_KEY`).
- `__init__.py` exports `PerplexityEmbeddings` and adds it to `__all__`.
- Unit tests under `tests/unit_tests/test_embeddings.py` covering
sync/async paths with mocked clients (no network).
- Integration tests under `tests/integration_tests/test_embeddings.py`,
gated on `PPLX_API_KEY` (matches the pattern in `test_search_api.py`).
- README updated to advertise the new component.
### Why
LangChain users already get chat, search, and tool wrappers from
`langchain-perplexity`, but had to drop down to the raw Perplexity SDK
to use embeddings. This closes that gap.
### References
- Perplexity Embeddings docs: https://docs.perplexity.ai/docs/embeddings
- Perplexity Embeddings API reference:
https://docs.perplexity.ai/api-reference/embeddings-post
### Issue
Closes#36726
## Testing
- `cd libs/partners/perplexity && make lint` — passes (ruff, format,
mypy).
- `cd libs/partners/perplexity && make test` — all unit tests pass (59
passed, 1 skipped).
- Integration tests will run in CI with secrets; they exercise real
`embed_documents` / `embed_query` / async variants against the live API
and assert vector dimensionality consistency.
---------
Co-authored-by: Claude Agent <agent@anthropic.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
## Description
Updates package metadata and README badges so LangChain social links
point to the new `@langchain_oss` X handle. This was completed with
AI-agent assistance.
## Test Plan
- [ ] Validate README badges and package metadata links point to
`https://x.com/langchain_oss`
_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>
The `MessageFinishData` TypedDict doesn't declare the `metadata` key
that the compat bridge injects at runtime. Cast to `dict[str, Any]` to
satisfy mypy, matching the existing pattern used for content blocks in
the same test.
Automated refresh of model profile data for all in-monorepo partner
integrations via `langchain-profiles refresh`.
🤖 Generated by the `refresh_model_profiles` workflow.
Co-authored-by: mdrxy <61371264+mdrxy@users.noreply.github.com>
Closes#37042
---
`AnthropicPromptCachingMiddleware` was unconditionally setting top-level
`cache_control` in `model_settings` for any `ChatAnthropic` subclass.
That field is direct-Anthropic-API only — `ChatAnthropicBedrock` (which
subclasses `ChatAnthropic` and passed the existing `isinstance` gate)
errored with `cache_control: Extra inputs are not permitted`.
Investigating that surfaced a related regression: PR #35967 also deleted
the block-level `cache_control` injection in `_get_request_payload`,
which silently disabled caching entirely for non-direct subclasses
(Bedrock had been falling back to in-block breakpoints). This restores
both paths.
## Changes
- Add `_is_direct_anthropic_llm_type` predicate that allowlists
`_llm_type == "anthropic-chat"`. Both the middleware's
`_supports_automatic_caching` and the new branch in
`ChatAnthropic._get_request_payload` route through it, so any subclass
that overrides `_llm_type` (Bedrock today, future direct-API variants
tomorrow) is treated as non-direct by default. Replaces the prior
substring-matching denylist on `"bedrock"`/`"vertex"`.
- Restore `_collect_code_execution_tool_ids`,
`_is_code_execution_related_block`, and a new
`_apply_cache_control_to_last_eligible_block` helper in `chat_models`.
For non-direct subclasses, `_get_request_payload` now pops
`cache_control` from kwargs and walks messages newest-to-oldest,
attaching the breakpoint to the last block that isn't
`code_execution`-related (Anthropic forbids breakpoints on those).
- Emit `UserWarning` when `cache_control` is requested but every
candidate block is `code_execution`-related — previously a silent drop.
- `AnthropicPromptCachingMiddleware._apply_caching` now sets the
top-level `cache_control` only when
`_supports_automatic_caching(request.model)`. System-message and
tool-definition breakpoints continue to apply for all `ChatAnthropic`
subclasses, since those are accepted by every transport.
- Note: `ChatAnthropicVertex` does not subclass `ChatAnthropic` (it
lives in `langchain-google-vertexai` and ships its own
`_get_request_payload`), so the chat-models changes here only affect
Bedrock. The middleware-side gate covers Vertex implicitly via the
`isinstance(request.model, ChatAnthropic)` check that already excludes
it.
Drop the `NotImplementedError` branch in `warn_deprecated` so callers
can pass `pending=False` without specifying a `removal` version. The
previous behavior contradicted the docstring (which claimed an empty
default would auto-compute a removal version) — no such computation
existed; the function just raised a placeholder "Need to determine which
default deprecation schedule to use" error.
Automated refresh of model profile data for all in-monorepo partner
integrations via `langchain-profiles refresh`.
🤖 Generated by the `refresh_model_profiles` workflow.
Co-authored-by: mdrxy <61371264+mdrxy@users.noreply.github.com>
## Summary
Fixes four issues in `get_separators_for_language()` in `character.py`:
- **Kotlin**: removed `"\ncase "` — `case` is not a Kotlin keyword.
Kotlin uses `when` expressions (already present in the list). This was
copied from Java/Swift.
- **Rust**: removed duplicate `"\nconst "` — appeared twice, once under
function definitions and again under control flow statements.
- **Haskell**: removed duplicate `"\n:: "` — appeared under function
definitions and again under type declarations.
- **Haskell**: removed duplicate `"\ndata "` — appeared under type
declarations and again under record field declarations.
All four are dead separators that never match or produce redundant
splits.
## Issue
Closes#37038
## Types of changes
- [x] Bug fix
## Checklist
- [x] I have read the CONTRIBUTING doc
- [x] Lint and unit tests pass locally with my changes
## Summary
Removes two incorrect separators from `get_separators_for_language()` in
`RecursiveCharacterTextSplitter`:
- **C#**: `"\nimplements "` is a Java keyword. C# uses `:` for interface
implementation. This separator never matches valid C# source code.
- **Elixir**: `"\nwhile "` does not exist in Elixir. The language uses
recursion and `Enum.reduce_while/3` instead of while loops.
Both are dead separators that silently degrade chunking quality by
occupying positions in the separator priority list without contributing
useful split points.
## Tests
Added two targeted tests:
- `test_csharp_separators_no_java_keywords`: verifies `"\nimplements "`
is not in the C# separator list
- `test_elixir_separators_no_while`: verifies `"\nwhile "` is not in the
Elixir separator list
Existing `test_csharp_code_splitter` continues to pass (no change to
expected output since `implements` never matched valid C# code).
Full suite: 129 passed, 0 failed.
Fixes#37030
## Summary
Stop inlining the full agent state into every tool-dispatch `Send` in
`create_agent`. Dispatch with the bare list form `Send("tools",
[tool_call])` and let `ToolNode` hydrate `ToolRuntime.state` from graph
channels at tool-execution time.
**Depends on**
[langchain-ai/langgraph#7594](https://github.com/langchain-ai/langgraph/pull/7594)
which teaches `ToolNode` to read channel state via `CONFIG_KEY_READ`
when given a bare tool-call list. `uv.lock` pins that branch for CI
while the langgraph PR is in flight — this pin will be reverted to a
published `langgraph` version before merge.
## What was happening
Before this change, every pending tool call produced a `Send` whose
payload was:
```python
ToolCallWithContext(
__type="tool_call_with_context",
tool_call=tool_call,
state=state, # ← the FULL agent state dict, including messages list
)
```
For any agent that runs many turns, `state["messages"]` grows linearly
with the conversation. Every super-step that dispatches tools serializes
that whole list into every `Send`, and those Sends live forever in the
checkpointer's `__pregel_tasks` writes. The result is **O(N²)
`__pregel_tasks` storage** across a run.
## What changed
- `libs/langchain_v1/langchain/agents/factory.py`:
- `_make_model_to_tools_edge` now returns `Send("tools", [tool_call])` —
no inlined state.
- Drops the `ToolCallWithContext` import.
- `libs/langchain_v1/pyproject.toml` + `libs/langchain_v1/uv.lock`:
- Temporary `[tool.uv.sources]` pin on `langgraph`,
`langgraph-prebuilt`, `langgraph-checkpoint` to the companion PR branch
so CI exercises both changes end-to-end. Revert after langgraph release.
## Why it's safe
- Same snapshot semantics as before. `Send` is emitted at the end of the
model super-step and consumed at the start of the tools super-step;
channels by that point reflect every write from the model super-step
(including the new AIMessage). Parallel tool tasks all see the same
values since sibling writes don't land until end-of-super-step.
- Legacy `ToolCallWithContext` input path is preserved in `ToolNode` —
no-op for any external caller still constructing it by hand.
## Test plan
- [x] `tests/unit_tests/agents/` — **738 passed, 2 skipped, 1 xfailed**
- [x] `ruff check .` / `ruff format .` — clean
- [x] `mypy langchain/agents/factory.py` — clean
- [x] Before/after benchmark (below)
## Benchmark
Script runs `create_agent` with a mock `GenericFakeChatModel` and two
tools (`write_file`, `edit_file`). Each of the N turns dispatches 2 tool
calls. After the run, the `InMemorySaver` is inspected for bytes stored
under `__pregel_tasks` — the channel that carries the tool-dispatch
`Send` payloads.
| N | TASKS before | TASKS after | ratio |
|---:|---:|---:|---:|
| 5 | 87.6 KB | **4.7 KB** | **18.6× smaller** |
| 10 | 335 KB | **9.4 KB** | **35.7× smaller** |
| 25 | 2.05 MB | **23.7 KB** | **86.5× smaller** |
| 50 | 8.14 MB | **47.6 KB** | **171× smaller** |
| 100 | 32.5 MB | **95.3 KB** | **341× smaller** |
| 200 | 130 MB | **192 KB** | **677× smaller** |
| 500 | 815 MB | **482 KB** | **1,691× smaller** |
**Growth shape:**
- **Before:** per-Send bytes scale with current `messages` length (full
state is inlined), so total TASKS across N turns = Σ(2 × k) for k=1..N ≈
O(N²).
- **After:** per-Send bytes are constant — just the `tool_call` dict.
Total TASKS is O(#dispatches), completely independent of conversation
length. In this bench with ~2 dispatches/turn: **940–964 bytes per turn
across N=5..500, essentially flat.**
An agent that makes 100 tool calls in a single turn pays the same TASKS
cost as one that makes 100 across 50 turns — which is the semantically
correct behavior.
Note: the `messages` channel is unchanged by this PR — it's still the
dominant storage term (growing O(N²) via `add_messages`). TASKS was a
second, compounding cost sitting on top of it; at N=100 it added 40% on
top of `messages`, at N=500 it added 67%. After the fix, TASKS is a
rounding error regardless of N.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Automated refresh of model profile data for all in-monorepo partner
integrations via `langchain-profiles refresh`.
🤖 Generated by the `refresh_model_profiles` workflow.
Co-authored-by: mdrxy <61371264+mdrxy@users.noreply.github.com>
Automated refresh of model profile data for all in-monorepo partner
integrations via `langchain-profiles refresh`.
🤖 Generated by the `refresh_model_profiles` workflow.
Co-authored-by: mdrxy <61371264+mdrxy@users.noreply.github.com>
Polish the manual release workflow (`_release.yml`) so the Actions UI is
readable at a glance — job display names, a run title that reflects the
actual published package, and a broader partner matrix for the
core-compat sanity check.
## Changes
- Add `name:` labels to each job (`📦 Build distribution`, `📝 Generate
release notes`, `🧪 Publish to TestPyPI`, `✅ Pre-release checks`, `🔄 Test
prior partners against new core`, `🚀 Publish to PyPI`, `🏷️ Tag GitHub
release`). Job IDs are unchanged, so all `needs:` references still
resolve.
- Rewrite `run-name` to resolve the dropdown value to the actual PyPI
package name — e.g. `core` → `langchain-core`, `openai` →
`langchain-openai`, with explicit remaps for the three that don't follow
`langchain-{name}` (`langchain` → `langchain-classic`, `langchain_v1` →
`langchain`, `standard-tests` → `langchain-tests`). `workflow_call`
callers passing full `libs/...` paths and manual overrides are returned
verbatim.
- Simplify `test-dependents` label to `🐍 Test dependent: ${{
matrix.package.path }} (Python ${{ matrix.python-version }})`.
we want to be able to test against the branch we run against when we are
testing external partner packages (aws, google) so overally the changes
on top of the external partners when we install the dependencies
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Automated refresh of model profile data for all in-monorepo partner
integrations via `langchain-profiles refresh`.
🤖 Generated by the `refresh_model_profiles` workflow.
Co-authored-by: mdrxy <61371264+mdrxy@users.noreply.github.com>