Forge better rankings from candidate documents with LLM reranking.
Project description
ranksmith
Forge better rankings from candidate documents.
ranksmith is a small Python package for LLM-based reranking. Version 1 focuses
on Azure OpenAI powered zero-shot reranking for candidate documents.
Highlights:
- Built-in listwise RankGPT, pairwise PRP, and tournament-style TourRank-r strategies
- Public strategy contracts for custom reranking methods
ModelClient/ModelProviderboundary for vendor-independent LLM calls- Strict JSON parsing and fast-fail error behavior
- Sync and async Azure OpenAI rerankers
- Reproducible benchmark summaries with committed evidence artifacts
Install
pip install ranksmith
Quick Start
from ranksmith import AzureOpenAIReranker, Document
reranker = AzureOpenAIReranker(
api_key="...",
azure_endpoint="https://example.openai.azure.com",
azure_deployment="gpt-4o-mini",
)
results = reranker.rerank(
query="What is listwise reranking?",
documents=[
Document(id="a", text="Listwise reranking compares candidates together."),
Document(id="b", text="Vector search retrieves candidate documents."),
],
top_k=2,
)
for result in results:
print(result.rank, result.original_index, result.document.id)
rank is 1-based for display. original_index is 0-based so it maps back to
the input list.
Supported Strategies & Algorithms
ranksmith separates the evaluation methodology (Strategy) from its execution
logic (Algorithm).
Recommended Use Cases
| Method | Strategy | Recommended when | Trade-off |
|---|---|---|---|
rankgpt_sliding_window |
ListwiseStrategy |
You need the default, lowest-friction LLM reranker for production or evaluation. | Low call count, but each prompt asks for a full ordered list and can be sensitive to output format. |
prp_sliding_k |
PairwiseStrategy |
You need pairwise preference comparisons or want to reproduce PRP-style behavior. | Many LLM calls; default passes=10 is expensive. |
tourrank_r, rounds=2 |
TourRankStrategy |
You want stronger quality than listwise on a moderate call budget. | More calls than RankGPT, much fewer than TourRank-10. |
tourrank_r, rounds=10 |
TourRankStrategy |
You are doing quality-focused offline reranking, paper-style evaluation, or final reranking where latency is acceptable. | Highest call cost among built-in methods in normal use. |
| Custom strategy | RerankStrategy / AsyncRerankStrategy |
You need deterministic business logic, a proprietary ranking process, or a new research method. | You own the ranking contract and validation behavior. |
Applying a Strategy
You can configure and inject a custom strategy into the AzureOpenAIReranker.
from ranksmith import AzureOpenAIReranker, ListwiseStrategy
strategy = ListwiseStrategy(
algorithm="rankgpt_sliding_window",
window_size=20,
stride=10,
max_document_chars=4000,
)
reranker = AzureOpenAIReranker(
api_key="...",
azure_endpoint="https://example.openai.azure.com",
azure_deployment="gpt-4o-mini",
strategy=strategy,
)
results = reranker.rerank("query", documents)
Pairwise PRP uses the same reranker facade with a different strategy:
from ranksmith import AzureOpenAIReranker, PairwiseStrategy
reranker = AzureOpenAIReranker(
api_key="...",
azure_endpoint="https://example.openai.azure.com",
azure_deployment="gpt-4o-mini",
strategy=PairwiseStrategy(passes=3),
)
TourRank-r uses the same injection point:
from ranksmith import AzureOpenAIReranker, TourRankStrategy
reranker = AzureOpenAIReranker(
api_key="...",
azure_endpoint="https://example.openai.azure.com",
azure_deployment="gpt-4o-mini",
strategy=TourRankStrategy(rounds=2, group_parallelism=1),
)
For quality-focused runs, explicitly switch to TourRank-10:
reranker = AzureOpenAIReranker(
api_key="...",
azure_endpoint="https://example.openai.azure.com",
azure_deployment="gpt-4o-mini",
strategy=TourRankStrategy(rounds=10),
)
Note: If
strategyis not provided, it defaults toListwiseStrategy(algorithm="rankgpt_sliding_window"). Pairwise PRP and TourRank-r use more LLM calls than listwise reranking, so check call estimates before live benchmarks.
Custom Strategies
Custom reranking methods should be implemented as new strategy classes instead
of adding new string values to ListwiseStrategy.algorithm. A strategy receives
the normalized Document objects, a model client, and optional top_k, then
returns RerankResult objects.
from collections.abc import Sequence
from ranksmith import (
AzureOpenAIReranker,
Document,
RerankResult,
)
class LengthStrategy:
def rerank(
self,
*,
query: str,
documents: Sequence[Document],
model_client: object,
top_k: int | None = None,
) -> list[RerankResult]:
del query, model_client
ordered_indexes = sorted(
range(len(documents)),
key=lambda index: len(documents[index].text),
reverse=True,
)
results = [
RerankResult(
document=documents[original_index],
rank=rank,
original_index=original_index,
metadata={"strategy": "length"},
)
for rank, original_index in enumerate(ordered_indexes, start=1)
]
return results if top_k is None else results[:top_k]
reranker = AzureOpenAIReranker(
api_key="...",
azure_endpoint="https://example.openai.azure.com",
azure_deployment="gpt-4o-mini",
strategy=LengthStrategy(),
)
Model-backed and async strategies use the same public contract. See the custom strategy extension guide and custom strategy example for the full extension guide.
Model Provider Architecture
ModelClient owns ranksmith's domain prompts and rank / compare / select
contracts. ModelProvider only executes vendor-specific JSON completion
requests.
| Layer | Responsibility | Public methods |
|---|---|---|
Strategy |
Build the final reranking order. | rerank(...) |
ModelClient |
Build ranksmith prompts, enforce the ranking domain contract, and emit usage. | rank(...), compare(...), select(...) |
ModelProvider |
Call a vendor SDK and return JSON completion text. | complete(...) |
from ranksmith import AzureAOAIProvider, ModelClient
provider = AzureAOAIProvider(
api_key="...",
azure_endpoint="https://example.openai.azure.com",
azure_deployment="gpt-4o-mini",
api_version="2024-08-01-preview",
)
model_client = ModelClient(provider=provider)
The same ModelClient can power all built-in strategies:
from ranksmith import AzureOpenAIReranker, PairwiseStrategy
reranker = AzureOpenAIReranker(
model_client=model_client,
strategy=PairwiseStrategy(passes=3),
)
OpenAIProvider, AnthropicProvider, and GeminiProvider are reserved public
stubs for future SDK-backed implementations. Calling them fails fast with
RerankProviderError.
Async Support
ranksmith provides first-class asynchronous support for high-throughput environments like FastAPI.
from ranksmith import AsyncAzureOpenAIReranker
reranker = AsyncAzureOpenAIReranker(
api_key="...",
azure_endpoint="https://example.openai.azure.com",
azure_deployment="gpt-4o-mini",
)
results = await reranker.rerank("query", documents)
Examples
Runnable examples live in the examples/ directory.
- rankgpt_sync.py: synchronous RankGPT integration
- rankgpt_async.py: async RankGPT integration
- pairwise_prp.py: pairwise PRP strategy
- tourrank.py: TourRank-r with a fake provider
- custom_strategy.py: custom strategy contracts
Benchmarking
The reference benchmark below measures reranking only. It uses the fixed native
MTEB AskUbuntuDupQuestions test candidates: 361 queries, 20 candidates per
query, shuffled with seed 13, using Azure OpenAI deployment gpt-5.4-nano.
Full command, call accounting, run scope, and artifact links are in the MTEB reranking benchmark notes.
| Method | NDCG@10 | MRR@10 | MAP | Recall@10 | p50 latency | Invalid rate | LLM calls/query | Total calls | Queries |
|---|---|---|---|---|---|---|---|---|---|
original |
0.3926 | 0.4594 | 0.3711 | 0.4993 | 0.0 ms | 0.000 | 0.0 | 0 | 361 |
rankgpt_sliding_window@20 |
0.6908 | 0.7470 | 0.6355 | 0.7671 | 1820.5 ms | 0.008 | 1.0 | 374 | 361 |
tourrank_r@20:r2 |
0.7023 | 0.7642 | 0.6421 | 0.7785 | 8297.1 ms | 0.000 | 8.0 | 2,888 | 361 |
tourrank_r@20:r10 |
0.7135 | 0.7734 | 0.6597 | 0.7836 | 39026.4 ms | 0.006 | 39.9 | 14,409 | 361 |
tourrank_r@20:r10 had the strongest scores in this run, while
tourrank_r@20:r2 stayed close with far fewer calls and lower latency. Full
prp_sliding_k@20 with the default passes=10 was not run in this full-query
benchmark; it would require 380 calls/query (137,180 calls over all 361
queries), so no quality or latency metrics are reported for that setting here.
The auxiliary prp_sliding_k@20:p1 result is documented in the benchmark
details as a reduced-budget call reference, not as the default PRP result.
Result Model
result.document # Document
result.rank # 1-based rank
result.original_index # 0-based input index
result.metadata # strategy-specific metadata
Error Handling
ranksmith fails fast. It does not silently truncate long documents, repair
invalid rankings, or return unvalidated LLM output.
from ranksmith import (
DocumentTooLongError,
RerankParseError,
RerankProviderError,
RerankStrategyError,
)
try:
results = reranker.rerank("query", documents)
except DocumentTooLongError:
...
except RerankParseError:
...
except RerankProviderError:
...
except RerankStrategyError:
...
Project details
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 ranksmith-0.4.0.tar.gz.
File metadata
- Download URL: ranksmith-0.4.0.tar.gz
- Upload date:
- Size: 1.8 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
197620f453041b2a7b93790c212a322911456440a6a4ae6d1581b6e69335c03d
|
|
| MD5 |
dcce2133d5e1657e64da59ceaf7a7cf9
|
|
| BLAKE2b-256 |
45bd8ed75b909a80ca2538c938a54627e4c77921507d3fb0b48097015b8f1323
|
Provenance
The following attestation bundles were made for ranksmith-0.4.0.tar.gz:
Publisher:
ci.yml on pko89403/ranksmith
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ranksmith-0.4.0.tar.gz -
Subject digest:
197620f453041b2a7b93790c212a322911456440a6a4ae6d1581b6e69335c03d - Sigstore transparency entry: 1581013688
- Sigstore integration time:
-
Permalink:
pko89403/ranksmith@12cf5f235604abe2e91756313405add7391f6ab6 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/pko89403
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@12cf5f235604abe2e91756313405add7391f6ab6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ranksmith-0.4.0-py3-none-any.whl.
File metadata
- Download URL: ranksmith-0.4.0-py3-none-any.whl
- Upload date:
- Size: 24.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68aa8139baf70443f3326699f11ad63c3650ceae812e5314deb675975e1f264f
|
|
| MD5 |
bf394f0d191d4eb4843c5a0d00769597
|
|
| BLAKE2b-256 |
26112a166299d28843c587c98147bb8d4f5538fac446ed1f72a6b31e06a83638
|
Provenance
The following attestation bundles were made for ranksmith-0.4.0-py3-none-any.whl:
Publisher:
ci.yml on pko89403/ranksmith
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ranksmith-0.4.0-py3-none-any.whl -
Subject digest:
68aa8139baf70443f3326699f11ad63c3650ceae812e5314deb675975e1f264f - Sigstore transparency entry: 1581013842
- Sigstore integration time:
-
Permalink:
pko89403/ranksmith@12cf5f235604abe2e91756313405add7391f6ab6 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/pko89403
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@12cf5f235604abe2e91756313405add7391f6ab6 -
Trigger Event:
push
-
Statement type: