langchain-reranker 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- langchain_reranker-0.1.0/PKG-INFO +72 -0
- langchain_reranker-0.1.0/README.md +56 -0
- langchain_reranker-0.1.0/pyproject.toml +32 -0
- langchain_reranker-0.1.0/pyproject.toml.orig +29 -0
- langchain_reranker-0.1.0/src/langchain_reranker/__init__.py +3 -0
- langchain_reranker-0.1.0/src/langchain_reranker/rerankers/__init__.py +15 -0
- langchain_reranker-0.1.0/src/langchain_reranker/rerankers/base.py +419 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: langchain-reranker
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add init_reranker to the LangChain ecosystem
|
|
5
|
+
Keywords: langchain,reranker,rerank,rag,retrieval
|
|
6
|
+
Author: Ashitaka-Laputa-IV
|
|
7
|
+
Author-email: Ashitaka-Laputa-IV <Ashitaka-Laputa-IV@outlook.com>
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
13
|
+
Requires-Dist: langchain-core>=1.0
|
|
14
|
+
Requires-Python: >=3.13
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# langchain-reranker
|
|
18
|
+
|
|
19
|
+
Add `init_reranker` to the LangChain ecosystem, following the same conventions as
|
|
20
|
+
`init_chat_model` and `init_embeddings`.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install langchain-reranker
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Provider integration packages are optional and installed on demand, e.g.
|
|
29
|
+
`pip install langchain-cohere`.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from langchain_reranker.rerankers import init_reranker
|
|
35
|
+
|
|
36
|
+
reranker = init_reranker("cohere:rerank-v3.5")
|
|
37
|
+
|
|
38
|
+
compressed = reranker.compress_documents(documents, query="what is langchain?")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`init_reranker` returns a `BaseDocumentCompressor`, so it plugs directly into
|
|
42
|
+
existing retrieval pipelines such as a contextual compression retriever.
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
reranker = init_reranker(model="jina-reranker-v2-base-multilingual", provider="jina")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Custom rerankers
|
|
49
|
+
|
|
50
|
+
Implement `rerank` and inherit `compress_documents` for free.
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from langchain_reranker.rerankers import BaseReranker, RerankResult
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class MyReranker(BaseReranker):
|
|
57
|
+
model: str = "my-reranker"
|
|
58
|
+
|
|
59
|
+
def rerank(self, query, documents, top_n=None):
|
|
60
|
+
scores = my_api(query, [doc.page_content for doc in documents])
|
|
61
|
+
ranked = sorted(zip(documents, scores), key=lambda pair: -pair[1])
|
|
62
|
+
return [
|
|
63
|
+
RerankResult(index=i, relevance_score=float(score), document=doc)
|
|
64
|
+
for i, (doc, score) in enumerate(ranked)
|
|
65
|
+
]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Local cross encoders
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
reranker = init_reranker("huggingface:BAAI/bge-reranker-v2-m3")
|
|
72
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# langchain-reranker
|
|
2
|
+
|
|
3
|
+
Add `init_reranker` to the LangChain ecosystem, following the same conventions as
|
|
4
|
+
`init_chat_model` and `init_embeddings`.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install langchain-reranker
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Provider integration packages are optional and installed on demand, e.g.
|
|
13
|
+
`pip install langchain-cohere`.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from langchain_reranker.rerankers import init_reranker
|
|
19
|
+
|
|
20
|
+
reranker = init_reranker("cohere:rerank-v3.5")
|
|
21
|
+
|
|
22
|
+
compressed = reranker.compress_documents(documents, query="what is langchain?")
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`init_reranker` returns a `BaseDocumentCompressor`, so it plugs directly into
|
|
26
|
+
existing retrieval pipelines such as a contextual compression retriever.
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
reranker = init_reranker(model="jina-reranker-v2-base-multilingual", provider="jina")
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Custom rerankers
|
|
33
|
+
|
|
34
|
+
Implement `rerank` and inherit `compress_documents` for free.
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from langchain_reranker.rerankers import BaseReranker, RerankResult
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MyReranker(BaseReranker):
|
|
41
|
+
model: str = "my-reranker"
|
|
42
|
+
|
|
43
|
+
def rerank(self, query, documents, top_n=None):
|
|
44
|
+
scores = my_api(query, [doc.page_content for doc in documents])
|
|
45
|
+
ranked = sorted(zip(documents, scores), key=lambda pair: -pair[1])
|
|
46
|
+
return [
|
|
47
|
+
RerankResult(index=i, relevance_score=float(score), document=doc)
|
|
48
|
+
for i, (doc, score) in enumerate(ranked)
|
|
49
|
+
]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Local cross encoders
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
reranker = init_reranker("huggingface:BAAI/bge-reranker-v2-m3")
|
|
56
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "langchain-reranker"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Add init_reranker to the LangChain ecosystem"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
keywords = [
|
|
8
|
+
"langchain",
|
|
9
|
+
"reranker",
|
|
10
|
+
"rerank",
|
|
11
|
+
"rag",
|
|
12
|
+
"retrieval",
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
20
|
+
]
|
|
21
|
+
dependencies = ["langchain-core>=1.0"]
|
|
22
|
+
|
|
23
|
+
[[project.authors]]
|
|
24
|
+
name = "Ashitaka-Laputa-IV"
|
|
25
|
+
email = "Ashitaka-Laputa-IV@outlook.com"
|
|
26
|
+
|
|
27
|
+
[dependency-groups]
|
|
28
|
+
dev = ["pytest>=8"]
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["uv_build>=0.12.23,<0.13.0"]
|
|
32
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "langchain-reranker"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Add init_reranker to the LangChain ecosystem"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Ashitaka-Laputa-IV", email = "Ashitaka-Laputa-IV@outlook.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.13"
|
|
10
|
+
keywords = ["langchain", "reranker", "rerank", "rag", "retrieval"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.13",
|
|
16
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"langchain-core>=1.0",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[dependency-groups]
|
|
23
|
+
dev = [
|
|
24
|
+
"pytest>=8",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["uv_build>=0.12.23,<0.13.0"]
|
|
29
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Entrypoint to using rerankers in LangChain."""
|
|
2
|
+
|
|
3
|
+
from langchain_reranker.rerankers.base import (
|
|
4
|
+
BaseReranker,
|
|
5
|
+
CrossEncoderReranker,
|
|
6
|
+
RerankResult,
|
|
7
|
+
init_reranker,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"BaseReranker",
|
|
12
|
+
"CrossEncoderReranker",
|
|
13
|
+
"RerankResult",
|
|
14
|
+
"init_reranker",
|
|
15
|
+
]
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
"""Factory function and base class for rerankers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
import importlib
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from collections.abc import Callable, Sequence
|
|
9
|
+
from typing import Any, ClassVar, override
|
|
10
|
+
|
|
11
|
+
from langchain_core.callbacks import Callbacks
|
|
12
|
+
from langchain_core.cross_encoders import BaseCrossEncoder
|
|
13
|
+
from langchain_core.documents import Document
|
|
14
|
+
from langchain_core.documents.compressor import BaseDocumentCompressor
|
|
15
|
+
from pydantic import BaseModel, ConfigDict
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RerankResult(BaseModel):
|
|
19
|
+
"""A single reranked document together with its relevance score."""
|
|
20
|
+
|
|
21
|
+
index: int
|
|
22
|
+
"""Index of the document in the input sequence."""
|
|
23
|
+
|
|
24
|
+
relevance_score: float
|
|
25
|
+
"""Relevance score assigned by the reranker. Higher means more relevant."""
|
|
26
|
+
|
|
27
|
+
document: Document
|
|
28
|
+
"""The reranked document."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BaseReranker(BaseDocumentCompressor, ABC):
|
|
32
|
+
"""Base class for rerankers.
|
|
33
|
+
|
|
34
|
+
Subclasses implement [`rerank`][langchain_reranker.rerankers.BaseReranker.rerank].
|
|
35
|
+
The inherited `compress_documents` method is implemented in terms of `rerank`, so a
|
|
36
|
+
reranker can be used anywhere a
|
|
37
|
+
[`BaseDocumentCompressor`][langchain_core.documents.compressor.BaseDocumentCompressor]
|
|
38
|
+
is expected (for example inside a contextual compression retriever).
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
model: str = ""
|
|
42
|
+
"""Name of the reranking model."""
|
|
43
|
+
|
|
44
|
+
top_n: int | None = None
|
|
45
|
+
"""Number of documents to return. If `None`, all documents are returned."""
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def rerank(
|
|
49
|
+
self,
|
|
50
|
+
query: str,
|
|
51
|
+
documents: Sequence[Document],
|
|
52
|
+
top_n: int | None = None,
|
|
53
|
+
) -> list[RerankResult]:
|
|
54
|
+
"""Rerank `documents` by relevance to `query`.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
query: The query to rank the documents against.
|
|
58
|
+
documents: The documents to rerank.
|
|
59
|
+
top_n: Number of results to return. Defaults to `self.top_n`.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
The reranked results, ordered from most to least relevant.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
@override
|
|
66
|
+
def compress_documents(
|
|
67
|
+
self,
|
|
68
|
+
documents: Sequence[Document],
|
|
69
|
+
query: str,
|
|
70
|
+
callbacks: Callbacks | None = None,
|
|
71
|
+
) -> Sequence[Document]:
|
|
72
|
+
"""Rerank documents and attach the relevance score to their metadata."""
|
|
73
|
+
_ = callbacks
|
|
74
|
+
return [
|
|
75
|
+
result.document.model_copy(
|
|
76
|
+
update={
|
|
77
|
+
"metadata": {
|
|
78
|
+
**result.document.metadata,
|
|
79
|
+
"relevance_score": result.relevance_score,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
for result in self.rerank(query=query, documents=documents, top_n=self.top_n)
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class CrossEncoderReranker(BaseReranker):
|
|
88
|
+
"""Reranker backed by a cross encoder.
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
```python
|
|
92
|
+
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
|
|
93
|
+
from langchain_reranker.rerankers import CrossEncoderReranker
|
|
94
|
+
|
|
95
|
+
cross_encoder = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-base")
|
|
96
|
+
reranker = CrossEncoderReranker(cross_encoder=cross_encoder, top_n=3)
|
|
97
|
+
reranker.rerank(query="...", documents=documents)
|
|
98
|
+
```
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
cross_encoder: BaseCrossEncoder
|
|
102
|
+
"""Cross encoder used to score query-document pairs."""
|
|
103
|
+
|
|
104
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
|
|
105
|
+
|
|
106
|
+
@override
|
|
107
|
+
def rerank(
|
|
108
|
+
self,
|
|
109
|
+
query: str,
|
|
110
|
+
documents: Sequence[Document],
|
|
111
|
+
top_n: int | None = None,
|
|
112
|
+
) -> list[RerankResult]:
|
|
113
|
+
"""Score each document against the query and sort by relevance."""
|
|
114
|
+
scores = self.cross_encoder.score(
|
|
115
|
+
[(query, document.page_content) for document in documents]
|
|
116
|
+
)
|
|
117
|
+
results = [
|
|
118
|
+
RerankResult(
|
|
119
|
+
index=index,
|
|
120
|
+
relevance_score=float(score),
|
|
121
|
+
document=document,
|
|
122
|
+
)
|
|
123
|
+
for index, (document, score) in enumerate(
|
|
124
|
+
zip(documents, scores, strict=False)
|
|
125
|
+
)
|
|
126
|
+
]
|
|
127
|
+
results.sort(key=lambda result: result.relevance_score, reverse=True)
|
|
128
|
+
limit = self.top_n if top_n is None else top_n
|
|
129
|
+
if limit is not None:
|
|
130
|
+
results = results[:limit]
|
|
131
|
+
return results
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _call(
|
|
135
|
+
cls: type[Any],
|
|
136
|
+
model: str,
|
|
137
|
+
**kwargs: Any,
|
|
138
|
+
) -> BaseDocumentCompressor:
|
|
139
|
+
return cls(model=model, **kwargs)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _call_cross_encoder(
|
|
143
|
+
cls: type[Any],
|
|
144
|
+
model: str,
|
|
145
|
+
*,
|
|
146
|
+
top_n: int | None = None,
|
|
147
|
+
**kwargs: Any,
|
|
148
|
+
) -> BaseReranker:
|
|
149
|
+
return CrossEncoderReranker(
|
|
150
|
+
cross_encoder=cls(model_name=model, **kwargs),
|
|
151
|
+
model=model,
|
|
152
|
+
top_n=top_n,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
_BUILTIN_PROVIDERS: dict[str, tuple[str, str, Callable[..., BaseDocumentCompressor]]] = {
|
|
157
|
+
"bedrock": ("langchain_aws", "AmazonBedrockRerank", _call),
|
|
158
|
+
"cohere": ("langchain_cohere", "CohereRerank", _call),
|
|
159
|
+
"dashscope": (
|
|
160
|
+
"langchain_community.document_compressors",
|
|
161
|
+
"DashScopeRerank",
|
|
162
|
+
_call,
|
|
163
|
+
),
|
|
164
|
+
"flashrank": (
|
|
165
|
+
"langchain_community.document_compressors",
|
|
166
|
+
"FlashrankRerank",
|
|
167
|
+
_call,
|
|
168
|
+
),
|
|
169
|
+
"huggingface": (
|
|
170
|
+
"langchain_huggingface",
|
|
171
|
+
"HuggingFaceCrossEncoder",
|
|
172
|
+
_call_cross_encoder,
|
|
173
|
+
),
|
|
174
|
+
"infinity": (
|
|
175
|
+
"langchain_community.document_compressors",
|
|
176
|
+
"InfinityRerank",
|
|
177
|
+
_call,
|
|
178
|
+
),
|
|
179
|
+
"jina": ("langchain_community.document_compressors", "JinaRerank", _call),
|
|
180
|
+
"local": (
|
|
181
|
+
"langchain_community.cross_encoders",
|
|
182
|
+
"HuggingFaceCrossEncoder",
|
|
183
|
+
_call_cross_encoder,
|
|
184
|
+
),
|
|
185
|
+
"openvino": (
|
|
186
|
+
"langchain_community.document_compressors",
|
|
187
|
+
"OpenVINOReranker",
|
|
188
|
+
_call,
|
|
189
|
+
),
|
|
190
|
+
"rankllm": (
|
|
191
|
+
"langchain_community.document_compressors",
|
|
192
|
+
"RankLLMRerank",
|
|
193
|
+
_call,
|
|
194
|
+
),
|
|
195
|
+
"volcengine": (
|
|
196
|
+
"langchain_community.document_compressors",
|
|
197
|
+
"VolcengineRerank",
|
|
198
|
+
_call,
|
|
199
|
+
),
|
|
200
|
+
"voyageai": ("langchain_voyageai", "VoyageAIRerank", _call),
|
|
201
|
+
}
|
|
202
|
+
"""Registry mapping provider names to their import configuration.
|
|
203
|
+
|
|
204
|
+
Each entry maps a provider key to a tuple of:
|
|
205
|
+
|
|
206
|
+
- `module_path`: The Python module path containing the reranker class.
|
|
207
|
+
- `class_name`: The name of the reranker class to import.
|
|
208
|
+
- `creator_func`: A callable that instantiates the class with provided kwargs.
|
|
209
|
+
|
|
210
|
+
!!! note
|
|
211
|
+
|
|
212
|
+
This dict is not exhaustive of all providers supported by LangChain, but is
|
|
213
|
+
meant to cover the most popular ones and serve as a template for adding more
|
|
214
|
+
providers in the future. If a provider is not in this dict, it can still be
|
|
215
|
+
used with `init_reranker` as long as its integration package is installed, but
|
|
216
|
+
the provider key will not be inferred from the model name and must be
|
|
217
|
+
specified explicitly via the `provider` parameter.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@functools.lru_cache(maxsize=len(_BUILTIN_PROVIDERS))
|
|
222
|
+
def _get_reranker_class_creator(
|
|
223
|
+
provider: str,
|
|
224
|
+
) -> Callable[..., BaseDocumentCompressor]:
|
|
225
|
+
"""Return a factory that creates a reranker for the given provider.
|
|
226
|
+
|
|
227
|
+
This function is cached to avoid repeated module imports.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
provider: The name of the provider (e.g., `'cohere'`, `'jina'`). Must be a
|
|
231
|
+
key in `_BUILTIN_PROVIDERS`.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
A callable that accepts model kwargs and returns a
|
|
235
|
+
`BaseDocumentCompressor` instance for the specified provider.
|
|
236
|
+
|
|
237
|
+
Raises:
|
|
238
|
+
ValueError: If the provider is not in `_BUILTIN_PROVIDERS`.
|
|
239
|
+
ImportError: If the provider's integration package is not installed.
|
|
240
|
+
"""
|
|
241
|
+
if provider not in _BUILTIN_PROVIDERS:
|
|
242
|
+
msg = (
|
|
243
|
+
f"Provider '{provider}' is not supported.\n"
|
|
244
|
+
f"Supported providers and their required packages:\n"
|
|
245
|
+
f"{_get_provider_list()}"
|
|
246
|
+
)
|
|
247
|
+
raise ValueError(msg)
|
|
248
|
+
|
|
249
|
+
module_name, class_name, creator_func = _BUILTIN_PROVIDERS[provider]
|
|
250
|
+
try:
|
|
251
|
+
module = importlib.import_module(module_name)
|
|
252
|
+
except ImportError as e:
|
|
253
|
+
pkg = module_name.split(".", maxsplit=1)[0].replace("_", "-")
|
|
254
|
+
msg = f"Could not import {pkg} python package. Please install it with `pip install {pkg}`"
|
|
255
|
+
raise ImportError(msg) from e
|
|
256
|
+
|
|
257
|
+
cls = getattr(module, class_name)
|
|
258
|
+
return functools.partial(creator_func, cls=cls)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _get_provider_list() -> str:
|
|
262
|
+
"""Get formatted list of providers and their packages."""
|
|
263
|
+
return "\n".join(
|
|
264
|
+
f" - {p}: {pkg[0].split('.', maxsplit=1)[0].replace('_', '-')}"
|
|
265
|
+
for p, pkg in _BUILTIN_PROVIDERS.items()
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _parse_model_string(model_name: str) -> tuple[str, str]:
|
|
270
|
+
"""Parse a model string into provider and model name components.
|
|
271
|
+
|
|
272
|
+
The model string should be in the format `'provider:model-name'`, where
|
|
273
|
+
provider is one of the supported providers.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
model_name: A model string in the format `'provider:model-name'`.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
A tuple of `(provider, model_name)`.
|
|
280
|
+
|
|
281
|
+
Raises:
|
|
282
|
+
ValueError: If the model string is not in the correct format or the
|
|
283
|
+
provider is unsupported.
|
|
284
|
+
"""
|
|
285
|
+
if ":" not in model_name:
|
|
286
|
+
msg = (
|
|
287
|
+
f"Invalid model format '{model_name}'.\n"
|
|
288
|
+
f"Model name must be in format 'provider:model-name'\n"
|
|
289
|
+
f"Example valid model strings:\n"
|
|
290
|
+
f" - cohere:rerank-v3.5\n"
|
|
291
|
+
f" - jina:jina-reranker-v2-base-multilingual\n"
|
|
292
|
+
f" - huggingface:BAAI/bge-reranker-v2-m3\n"
|
|
293
|
+
f"Supported providers: {_BUILTIN_PROVIDERS.keys()}"
|
|
294
|
+
)
|
|
295
|
+
raise ValueError(msg)
|
|
296
|
+
|
|
297
|
+
provider, model = model_name.split(":", 1)
|
|
298
|
+
provider = provider.lower().strip()
|
|
299
|
+
model = model.strip()
|
|
300
|
+
|
|
301
|
+
if provider not in _BUILTIN_PROVIDERS:
|
|
302
|
+
msg = (
|
|
303
|
+
f"Provider '{provider}' is not supported.\n"
|
|
304
|
+
f"Supported providers and their required packages:\n"
|
|
305
|
+
f"{_get_provider_list()}"
|
|
306
|
+
)
|
|
307
|
+
raise ValueError(msg)
|
|
308
|
+
if not model:
|
|
309
|
+
msg = "Model name cannot be empty"
|
|
310
|
+
raise ValueError(msg)
|
|
311
|
+
return provider, model
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _infer_model_and_provider(
|
|
315
|
+
model: str,
|
|
316
|
+
*,
|
|
317
|
+
provider: str | None = None,
|
|
318
|
+
) -> tuple[str, str]:
|
|
319
|
+
if not model.strip():
|
|
320
|
+
msg = "Model name cannot be empty"
|
|
321
|
+
raise ValueError(msg)
|
|
322
|
+
if provider is None and ":" in model:
|
|
323
|
+
provider, model_name = _parse_model_string(model)
|
|
324
|
+
else:
|
|
325
|
+
model_name = model
|
|
326
|
+
|
|
327
|
+
if not provider:
|
|
328
|
+
msg = (
|
|
329
|
+
"Must specify either:\n"
|
|
330
|
+
"1. A model string in format 'provider:model-name'\n"
|
|
331
|
+
" Example: 'cohere:rerank-v3.5'\n"
|
|
332
|
+
"2. Or explicitly set provider from: "
|
|
333
|
+
f"{_BUILTIN_PROVIDERS.keys()}"
|
|
334
|
+
)
|
|
335
|
+
raise ValueError(msg)
|
|
336
|
+
|
|
337
|
+
if provider not in _BUILTIN_PROVIDERS:
|
|
338
|
+
msg = (
|
|
339
|
+
f"Provider '{provider}' is not supported.\n"
|
|
340
|
+
f"Supported providers and their required packages:\n"
|
|
341
|
+
f"{_get_provider_list()}"
|
|
342
|
+
)
|
|
343
|
+
raise ValueError(msg)
|
|
344
|
+
return provider, model_name
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def init_reranker(
|
|
348
|
+
model: str,
|
|
349
|
+
*,
|
|
350
|
+
provider: str | None = None,
|
|
351
|
+
**kwargs: Any,
|
|
352
|
+
) -> BaseDocumentCompressor:
|
|
353
|
+
"""Initialize a reranker from a model name and optional provider.
|
|
354
|
+
|
|
355
|
+
!!! note
|
|
356
|
+
|
|
357
|
+
Requires the integration package for the chosen provider to be installed.
|
|
358
|
+
|
|
359
|
+
See the `provider` parameter below for specific package names
|
|
360
|
+
(e.g., `pip install langchain-cohere`).
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
model: The name of the model, e.g. `'cohere:rerank-v3.5'`.
|
|
364
|
+
|
|
365
|
+
You can also specify model and provider in a single argument using
|
|
366
|
+
the `'{provider}:{model}'` format, e.g. `'cohere:rerank-v3.5'`.
|
|
367
|
+
provider: The provider if not specified as part of the model arg.
|
|
368
|
+
|
|
369
|
+
Supported `provider` values and the corresponding integration package
|
|
370
|
+
are:
|
|
371
|
+
|
|
372
|
+
- `bedrock` -> `langchain-aws`
|
|
373
|
+
- `cohere` -> `langchain-cohere`
|
|
374
|
+
- `dashscope` -> `langchain-community`
|
|
375
|
+
- `flashrank` -> `langchain-community`
|
|
376
|
+
- `huggingface` -> `langchain-huggingface`
|
|
377
|
+
- `infinity` -> `langchain-community`
|
|
378
|
+
- `jina` -> `langchain-community`
|
|
379
|
+
- `local` -> `langchain-community` (sentence-transformers)
|
|
380
|
+
- `openvino` -> `langchain-community`
|
|
381
|
+
- `rankllm` -> `langchain-community`
|
|
382
|
+
- `volcengine` -> `langchain-community`
|
|
383
|
+
- `voyageai` -> `langchain-voyageai`
|
|
384
|
+
|
|
385
|
+
**kwargs: Additional model-specific parameters passed to the reranker.
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
A `BaseDocumentCompressor` that reranks documents for a query.
|
|
389
|
+
|
|
390
|
+
Raises:
|
|
391
|
+
ValueError: If the provider is not supported or cannot be determined.
|
|
392
|
+
ImportError: If the required provider package is not installed.
|
|
393
|
+
|
|
394
|
+
???+ example
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
# pip install langchain-reranker langchain-cohere
|
|
398
|
+
|
|
399
|
+
# Using a model string
|
|
400
|
+
reranker = init_reranker("cohere:rerank-v3.5")
|
|
401
|
+
reranker.compress_documents(documents, query="...")
|
|
402
|
+
|
|
403
|
+
# Using an explicit provider
|
|
404
|
+
reranker = init_reranker(model="jina-reranker-v2-base-multilingual", provider="jina")
|
|
405
|
+
|
|
406
|
+
# With additional parameters
|
|
407
|
+
reranker = init_reranker("cohere:rerank-v3.5", top_n=5)
|
|
408
|
+
```
|
|
409
|
+
"""
|
|
410
|
+
if not model:
|
|
411
|
+
providers = ", ".join(_BUILTIN_PROVIDERS.keys())
|
|
412
|
+
msg = f"Must specify model name. Supported providers are: {providers}"
|
|
413
|
+
raise ValueError(msg)
|
|
414
|
+
|
|
415
|
+
provider, model_name = _infer_model_and_provider(model, provider=provider)
|
|
416
|
+
return _get_reranker_class_creator(provider)(model=model_name, **kwargs)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
__all__ = ["BaseReranker", "CrossEncoderReranker", "RerankResult", "init_reranker"]
|