langchain-turn-window
A small, model-agnostic LangChain agent middleware that keeps only the last N conversational turns before each model call — without ever splitting an AIMessage tool call from its ToolMessage response.
Why
Every provider (OpenAI, Anthropic, Gemini, ...) rejects a request where a tool_calls entry has no matching tool result message. Naive history trimming — slicing by raw message count or token count — can easily cut the window in the middle of a tool-call/tool-result pair and break the request.
TurnWindowMiddleware anchors the window on HumanMessage boundaries instead: it always starts the kept slice at the Nth-most-recent HumanMessage, which guarantees every AIMessage tool call inside the window still has its ToolMessage response attached.
This is a lightweight alternative to LangChain's built-in SummarizationMiddleware (which needs an extra LLM call) or context_editing (which selectively clears old tool outputs) for the common case of "just bound the context to the last few turns."
Install
pip install langchain-turn-window
Usage
from langchain.agents import create_agent
from langchain_turn_window import TurnWindowMiddleware
agent = create_agent(
model="openai:gpt-4o",
tools=[...],
middleware=[
TurnWindowMiddleware(keep_last_turns=3),
],
)
keep_last_turns is the number of most-recent HumanMessage turns to retain. If the conversation has fewer turns than that, the full history passes through unchanged.
How it works
TurnWindowMiddleware implements wrap_model_call / awrap_model_call. On every model call it:
- Finds the indexes of all
HumanMessages inrequest.messages. - If there are fewer than
keep_last_turnshuman turns, passes the messages through unchanged. - Otherwise, slices the message list starting at the
keep_last_turns-th-most-recentHumanMessageand forwards only that suffix to the model.
The underlying pure function is also exported directly if you want to use it outside of middleware (e.g. in a custom node):
from langchain_turn_window import window_by_human_turns
windowed = window_by_human_turns(messages, n=3)
window_by_human_turns never mutates its input and returns the original list object unchanged when no trimming is needed (positive-n guard, empty list, or fewer human turns than n).
Configuration
| Constructor arg | Default | Description |
|---|---|---|
keep_last_turns |
3 |
Number of most-recent human turns to keep. Must be >= 1. |
Limitations
- Assumes a single-user conversational shape (one
HumanMessageper turn). For multi-agent handoff patterns where turns aren't anchored onHumanMessage, this middleware is not the right fit as-is. - This is a hard trim, not a summary — content outside the window is dropped, not condensed. Combine with
SummarizationMiddlewareif you need older context preserved in compressed form.
Development
pip install -e ".[dev]"
pytest
License
MIT
Metadata
Release files for langchain-turn-window 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| langchain_turn_window-0.1.0.tar.gz | 5.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| langchain_turn_window-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 11.2 kB
Release files / langchain_turn_window-0.1.0.tar.gz
| Download URL | langchain_turn_window-0.1.0.tar.gz |
|---|---|
| Size | 5.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
36173aef0da9b49e0fc2d0f2b7a77fef891841a77a2e78c95d3261f89b1a87d1
|
|
BLAKE2b-256 checksum How to use checksums |
4ede5511ab155d24629a48e10cf629e6d568209cb17a133b8b24e659d052139a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Release files / langchain_turn_window-0.1.0-py3-none-any.whl
| Download URL | langchain_turn_window-0.1.0-py3-none-any.whl |
|---|---|
| Size | 5.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1ee10be8cd87679ec83f5ba5de42933f1b5397069db99bbff8ad4fe730f20126
|
|
BLAKE2b-256 checksum How to use checksums |
29495c34e22c1965c050ae356f57729dcf9419617c6a31937bae81870e4fd3dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|
Security for langchain-turn-window 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.