llama-index-postprocessor-needlepath
Needlepath context selection as a LlamaIndex BaseNodePostprocessor: given the
retrieved nodes and the query, keep the nodes that carry the answer and replace
each one's text with the part of it that does.
pip install llama-index-postprocessor-needlepath
from llama_index.postprocessor.needlepath import NeedlepathPostprocessor
query_engine = index.as_query_engine(
node_postprocessors=[NeedlepathPostprocessor(operating_point="np-2026-07-r2")],
)
The key comes from NEEDLEPATH_API_KEY. If the service is slow, down, or stands
down, the query engine gets the nodes it was given, unchanged.
Scope, stated honestly
This is a RAG-retrieval seam. It never sees tool outputs, so it exercises the weakest of Needlepath's use cases — retrieved chunks are already short and already ranked. It exists because LlamaIndex is where a large number of developers already have a postprocessor slot, and an integration that does not exist cannot be evaluated. If you are choosing where to try Needlepath first and you have an agent with tool calls, start there instead.
Configuration
| Parameter | Default | What it does |
|---|---|---|
operating_point |
— | Required. Immutable label. Also NEEDLEPATH_OPERATING_POINT. |
max_context_tokens |
4000 |
Budget for the selected context, and the default trigger. |
trigger_tokens |
0 |
Skip the call below this. 0 means "use the budget". |
excerpt |
True |
Replace kept nodes' text with their excerpt. False filters only. |
update_scores |
False |
Replace NodeWithScore.score with the selection score. |
preserve_input_order |
False |
Return kept nodes in retrieval order rather than ranked order. |
shadow |
False |
Measure and report; change nothing. |
enabled |
True |
Kill switch. Needs no credentials when False. |
Any other keyword is forwarded to the core client (base_url, api_key,
timeout, …).
Two LlamaIndex-specific decisions
Scores are not overwritten by default. A reranker replaces
NodeWithScore.score with its own. Ours is not calibrated against your
retriever's similarity, and silently substituting one for the other corrupts any
downstream threshold or similarity_cutoff. The selection score is available as
node metadata; set update_scores=True if you want reranker-like behaviour and
know what your thresholds mean.
Provenance metadata is hidden from the model. Node metadata is interpolated
into the prompt under MetadataMode.LLM and into embeddings under
MetadataMode.EMBED. Every needlepath_* key this package writes is added to
excluded_llm_metadata_keys and excluded_embed_metadata_keys on the copy, so
debug data never reaches the model or an index.
Node identity survives
A rewritten node is a model_copy(deep=True) with set_content() applied, so
id_, metadata, relationships and therefore ref_doc_id are carried over
verbatim. Building a fresh TextNode would mint a new random id_ and drop
relationships, severing ref_doc_id, dedup and PrevNextNodePostprocessor.
The nodes you pass in are never mutated.
Fail open
| What happens | What the query engine gets |
|---|---|
| Timeout, 5xx, throttling, contract violation | the original nodes, unchanged |
| The gate stands down, or selects nothing | the original nodes, unchanged |
No QueryBundle |
the original nodes, unchanged |
| Every returned id is unrecognized | the original nodes, unchanged |
The no-query case is worth calling out: every shipped reranker raises
ValueError("Missing query bundle in extra info.") there. This does not. A
postprocessor that raises turns a missing query into a failed query for the whole
engine, and the fail-open rule is binding at a framework boundary.
postprocessor.stats.as_dict() aggregates the counters.
Async
_apostprocess_nodes is overridden rather than inherited. The base class's
default is asyncio.to_thread(self._postprocess_nodes, …), which is correct but
burns a thread per query on what is a network call — and nine call sites in
llama-index-core reach the async path, including RetrieverQueryEngine and
every chat engine.
Packaging
Named llama-index-postprocessor-needlepath rather than needlepath-llamaindex.
That is the one naming exception to Needlepath's vendor-first convention: a
package outside the host convention is not discoverable on LlamaHub at all.
PEP 420 namespace layout — llama_index/ and llama_index/postprocessor/ carry
no __init__.py, so this distribution coexists with llama-index-core and every
other integration under the same directories.
Tested against
llama-index-core==0.14.23. Range >=0.13.0,<0.15, matching every shipped
llama-index-postprocessor-* package. CI runs against the newest minor.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llama_index_postprocessor_needlepath-0.1.0.tar.gz.
File metadata
- Download URL: llama_index_postprocessor_needlepath-0.1.0.tar.gz
- Upload date:
- Size: 11.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90ceaa5e0e4458db7c0657b87cd5d48ad85a239d364b4080528a62e0289ff97d
|
|
| MD5 |
dbd3253acac765581de855613053de37
|
|
| BLAKE2b-256 |
9b6c987e68ca041e9ce5ec839c607ce0d74da3014b5f77d3117cf5d1b82a484e
|
File details
Details for the file llama_index_postprocessor_needlepath-0.1.0-py3-none-any.whl.
File metadata
- Download URL: llama_index_postprocessor_needlepath-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81e952b259c56671f01417c574a7f79d93e2dbccdba06ec25f5679601f17d817
|
|
| MD5 |
811906ec76430fc800c9b78c0d3bad64
|
|
| BLAKE2b-256 |
bbed318ec37274251c3870c9509f7e326e433ff5a74dbedff4dd3db857acaf2d
|