jev·agent
Get API access

Integration

Jev with LlamaIndex

No package exists for this either — but unlike a chain framework, a retrieval framework has an obvious seam for a decision model. Retrieval returns candidates; something has to judge them. That judgement is exactly what Jev sells.

The seam is the postprocessor

A retrieval pipeline is retrieve, then rerank, then synthesise. The middle step is a filter over candidates you already have — a bounded set, one judgement each, no text to produce. That is a decision model's home ground, and it is the one place in a RAG stack where Jev is not a substitute for something but a better fit than what is usually there.

TypeSafe evidently agrees: its own documentation ships a cookbook on classifying RAG passages, and recommends a Noul as the relevance filter when you cannot narrow the state any other way.

Embeddings

Cheap, fast, and answers only one question: what looks similar to this?

Cross-encoder

Stronger ordering among near-duplicates. An opaque score, and another model to host.

Jev

Ask the actual question — does this passage answer it? — and get a calibrated probability you can threshold.

A node postprocessor

Two questions per node, asked in one call each: is it relevant at all, and how completely does it answer the question. The first is a filter, the second is the ordering.

python
from llama_index.core.postprocessor.types import BaseNodePostprocessor
from llama_index.core.schema import NodeWithScore, QueryBundle
from typesafe_sdk import Noul, Score, TypeSafeClient

class JevRerank(BaseNodePostprocessor):
    """Rerank retrieved nodes by asking about the query, not about distance."""

    top_n: int = 5
    min_relevance: float = 0.5

    def _postprocess_nodes(self, nodes, query_bundle: QueryBundle | None = None):
        if not query_bundle:
            return nodes
        client = TypeSafeClient()
        question = query_bundle.query_str
        rescored: list[NodeWithScore] = []

        for node in nodes:
            answers = client.system_one(
                model="jev-latest",
                # Only the passage and the question. Nothing else from the pipeline:
                # unrelated state is a documented accuracy cost.
                state={"question": question, "passage": node.node.get_content()},
                questions={
                    "relevant": Noul(
                        instructions="Does this passage contain information that answers the question?",
                    ),
                    "completeness": Score(
                        instructions="How completely does the passage answer the question?",
                        criteria=["Not at all", "A fragment", "Most of it", "Fully"],
                    ),
                },
            ).answers

            if answers["relevant"].noul < self.min_relevance:
                continue  # dropped, not demoted — irrelevant context costs accuracy downstream
            node.score = answers["completeness"].score
            rescored.append(node)

        rescored.sort(key=lambda n: n.score or 0.0, reverse=True)
        return rescored[: self.top_n]

Read the Score correctly

The single most common mistake with this pattern. A Score is not 0–1. It runs 0..n−1 across the rubric you wrote and it is literally the probability-weighted mean of the level indices — so the four levels above return something in 0..3, and a node scoring 2.68 sits between “Most of it” and “Fully”, leaning towards fully.

That makes it a good sort key and a bad percentage. If you need a 0–1 number for a UI, divide by len(criteria) - 1 and say so in the label. Probabilities come back keyed "0", "1", "2" — never by the label text.

What it costs, per query

Reranking is a per-candidate cost, so it is worth doing the arithmetic before you put it in a hot path. Twenty retrieved nodes of 500 tokens each:

text
20 nodes × 500 tokens = 10,000 tokens
10,000 / 1,000,000 × $0.042 = $0.00042 per query
                            ≈ $0.42 per 1,000 queries

Both questions ride in the same call per node, so asking about completeness as well as relevance is free — the state is ingested once and every question is evaluated against it in parallel. The cost is set by how much text you send, not by how much you ask about it. If you want it cheaper, truncate the passages; asking fewer questions saves nothing.

Common questions

Is there a LlamaIndex integration for Jev?

Not as of this page's last check — no first-party package from TypeSafe and none listed in awesome-jev. The integration surface you want is a node postprocessor, which is about thirty lines.

Can Jev replace my reranker?

For relevance filtering, often yes: you get a calibrated probability per node instead of an opaque similarity score, and you can ask about the actual question rather than about vector distance. For fine-grained ordering among near-identical passages, a purpose-built cross-encoder reranker is still stronger.

Is it cheaper than an LLM reranker?

Substantially. Input is $0.042 per million tokens with no output charge, so reranking a 500-token chunk costs about $0.000021. An LLM reranker pays for reasoning tokens on every chunk.