np-2026-08-r4, the newest published label. If
you already run an older label, evaluate before switching.
The core client
select() never raises, and never rejects. Read result.applied; when it is
false, use your original context. That is the whole integration contract, and it
is why the error taxonomy in Errors is something you read when you are
curious rather than something you branch on.
Python is 3.9+ and installs no dependencies: the default transport is stdlib
urllib, so adding the client cannot move a version you already pinned.
pip install "needlepath[httpx]" adds an httpx-backed async transport, and
AsyncNeedlepathClient has identical semantics either way. The TypeScript client
runs on Node ≥ 20.19, Cloudflare workerd and Vercel’s Edge Runtime, and ships
dual ESM + CJS.
Framework adapters
- LangChain
- LiteLLM
- LlamaIndex
wrap_tool_call catches a
single oversized tool result on the way into the message list: a tool returns
40 KB of JSON and what lands is the part answering the current step. It engages
only above tool_result_max_tokens (default 2000); below that no call is made
and nothing is touched.wrap_model_call selects over the tool results already accumulated in history,
against the current task, and engages above history_max_tokens (default
8000). A result that mattered three steps ago and does not now collapses to a
placeholder.Writing it yourself
If you are not on Python or TypeScript, the integration surface is the HTTP contract and it is smaller than it looks. It is three things: map your messages to records, call the endpoint, and fail open.- Hold back what must survive.
required_record_idsis the field designed to pin a record and it does not pin today. Keeping a message out ofrecords[]is the only guarantee, and it is the right shape anyway: a system prompt does not benefit from being selected. - Supply an
idon every record. Without one you get a server-generated id back that joins to nothing on your side, and two identical calls return different ids. - Two pass-through paths: an exception, and an empty selection. Both return the original list. An empty selection returned as a success is a failure mode, not a valid answer.
Keep the message order. The example filters the original list rather than
rebuilding from
selected[], so ordering is preserved for free.
selected[] is in selection order, not conversation order, and reconstructing
from it directly will scramble a conversation.The LangGraph seam
For a LangGraph graph, the seam that composes with a non-destructive selection service is a pre-model hook that changes what the model sees without mutating graph state, thellm_input_messages pattern. Selection is advisory
per call, so a later node still has the full state to work from.
Deleting messages from state with RemoveMessage also works and is the right
tool if you genuinely want them gone, but it is destructive: a stand-down cannot
undo it, and a subsequent turn cannot recover a record the engine would have
selected next time.
Rules every adapter follows
These are binding on our packages and are the right rules for yours.1
Fail open on every error path
Timeout, non-2xx, empty selection: pass the original messages through
unmodified and emit a metadata-only warning. At a framework boundary this
matters more than anywhere else, because the caller has no way to inspect what
happened.
2
Pin the operating point in the constructor
Not per call, and never left to the server default. Behaviour must not shift
under an application that has not changed.
3
Guard the 6 MB body limit client-side
It is a platform cap enforced before the service is reached, so the error it
produces will not be a Needlepath error shape. Check before sending.
4
Depend only on public, documented framework symbols
And cap the framework at a major version. Reaching into a framework’s internals
buys a feature now and an outage on its next minor release.
Choosing between us and what your framework already ships
Several frameworks now include some form of context trimming in-tree and free: LangChain, for example, ships middleware that clears tool results once a token threshold is crossed. A developer evaluating Needlepath usually already has that installed, so the honest comparison is not “can something prune tool results”. It is three narrower questions:
If your problem is “the context window overflows and I need it to stop”, a
threshold-based trimmer in your framework is a genuinely reasonable answer and
it costs nothing. Reach for Needlepath when which records survive is the part
that matters.
Try it on your own traffic first
Run selection alongside your existing prompts, record what would have been
dropped, and change nothing until you have looked.