Skip to main content
Needlepath ships a client for Python and TypeScript, and three framework adapters. Every one of them is Apache-2.0 and fails open: if the service is slow, down, or stands down, your original context goes through unchanged. The snippets on this page pin 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

Two seams, and they engage on different problems. 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.
Three details in there are the whole art of it:
  • Hold back what must survive. required_record_ids is the field designed to pin a record and it does not pin today. Keeping a message out of records[] is the only guarantee, and it is the right shape anyway: a system prompt does not benefit from being selected.
  • Supply an id on 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, the llm_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.