AutoRAG 0.0.0__py3-none-any.whl
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.
- autorag/__init__.py +82 -0
- autorag/chunker.py +51 -0
- autorag/cli.py +209 -0
- autorag/dashboard.py +199 -0
- autorag/data/__init__.py +109 -0
- autorag/data/chunk/__init__.py +2 -0
- autorag/data/chunk/base.py +128 -0
- autorag/data/chunk/langchain_chunk.py +76 -0
- autorag/data/chunk/llama_index_chunk.py +96 -0
- autorag/data/chunk/run.py +38 -0
- autorag/data/legacy/__init__.py +0 -0
- autorag/data/legacy/corpus/__init__.py +2 -0
- autorag/data/legacy/corpus/langchain.py +47 -0
- autorag/data/legacy/corpus/llama_index.py +93 -0
- autorag/data/legacy/qacreation/__init__.py +6 -0
- autorag/data/legacy/qacreation/base.py +239 -0
- autorag/data/legacy/qacreation/llama_index.py +253 -0
- autorag/data/legacy/qacreation/llama_index_default_prompt.txt +54 -0
- autorag/data/legacy/qacreation/ragas.py +75 -0
- autorag/data/legacy/qacreation/simple.py +99 -0
- autorag/data/parse/__init__.py +1 -0
- autorag/data/parse/base.py +79 -0
- autorag/data/parse/clova.py +194 -0
- autorag/data/parse/langchain_parse.py +87 -0
- autorag/data/parse/llamaparse.py +126 -0
- autorag/data/parse/run.py +141 -0
- autorag/data/parse/table_hybrid_parse.py +134 -0
- autorag/data/qa/__init__.py +3 -0
- autorag/data/qa/evolve/__init__.py +0 -0
- autorag/data/qa/evolve/llama_index_query_evolve.py +64 -0
- autorag/data/qa/evolve/openai_query_evolve.py +81 -0
- autorag/data/qa/evolve/prompt.py +288 -0
- autorag/data/qa/extract_evidence.py +1 -0
- autorag/data/qa/filter/__init__.py +0 -0
- autorag/data/qa/filter/dontknow.py +117 -0
- autorag/data/qa/filter/passage_dependency.py +88 -0
- autorag/data/qa/filter/prompt.py +73 -0
- autorag/data/qa/generation_gt/__init__.py +0 -0
- autorag/data/qa/generation_gt/base.py +16 -0
- autorag/data/qa/generation_gt/llama_index_gen_gt.py +41 -0
- autorag/data/qa/generation_gt/openai_gen_gt.py +84 -0
- autorag/data/qa/generation_gt/prompt.py +27 -0
- autorag/data/qa/query/__init__.py +0 -0
- autorag/data/qa/query/llama_gen_query.py +82 -0
- autorag/data/qa/query/openai_gen_query.py +95 -0
- autorag/data/qa/query/prompt.py +201 -0
- autorag/data/qa/sample.py +26 -0
- autorag/data/qa/schema.py +322 -0
- autorag/data/utils/__init__.py +0 -0
- autorag/data/utils/util.py +103 -0
- autorag/deploy/__init__.py +9 -0
- autorag/deploy/api.py +303 -0
- autorag/deploy/base.py +235 -0
- autorag/deploy/gradio.py +74 -0
- autorag/deploy/swagger.yml +202 -0
- autorag/embedding/__init__.py +0 -0
- autorag/embedding/base.py +144 -0
- autorag/embedding/vllm.py +256 -0
- autorag/evaluation/__init__.py +3 -0
- autorag/evaluation/generation.py +88 -0
- autorag/evaluation/metric/__init__.py +22 -0
- autorag/evaluation/metric/deepeval_prompt.py +322 -0
- autorag/evaluation/metric/g_eval_prompts/coh_detailed.txt +32 -0
- autorag/evaluation/metric/g_eval_prompts/con_detailed.txt +33 -0
- autorag/evaluation/metric/g_eval_prompts/flu_detailed.txt +26 -0
- autorag/evaluation/metric/g_eval_prompts/rel_detailed.txt +33 -0
- autorag/evaluation/metric/generation.py +504 -0
- autorag/evaluation/metric/retrieval.py +115 -0
- autorag/evaluation/metric/retrieval_contents.py +65 -0
- autorag/evaluation/metric/util.py +88 -0
- autorag/evaluation/retrieval.py +83 -0
- autorag/evaluation/retrieval_contents.py +65 -0
- autorag/evaluation/util.py +43 -0
- autorag/evaluator.py +559 -0
- autorag/node_line.py +65 -0
- autorag/nodes/__init__.py +0 -0
- autorag/nodes/generator/__init__.py +4 -0
- autorag/nodes/generator/base.py +103 -0
- autorag/nodes/generator/llama_index_llm.py +169 -0
- autorag/nodes/generator/openai_llm.py +329 -0
- autorag/nodes/generator/run.py +148 -0
- autorag/nodes/generator/vllm.py +147 -0
- autorag/nodes/generator/vllm_api.py +191 -0
- autorag/nodes/hybridretrieval/__init__.py +2 -0
- autorag/nodes/hybridretrieval/base.py +58 -0
- autorag/nodes/hybridretrieval/hybrid_cc.py +227 -0
- autorag/nodes/hybridretrieval/hybrid_rrf.py +149 -0
- autorag/nodes/hybridretrieval/run.py +137 -0
- autorag/nodes/lexicalretrieval/__init__.py +1 -0
- autorag/nodes/lexicalretrieval/bm25.py +381 -0
- autorag/nodes/lexicalretrieval/run.py +148 -0
- autorag/nodes/passageaugmenter/__init__.py +2 -0
- autorag/nodes/passageaugmenter/base.py +76 -0
- autorag/nodes/passageaugmenter/pass_passage_augmenter.py +43 -0
- autorag/nodes/passageaugmenter/prev_next_augmenter.py +155 -0
- autorag/nodes/passageaugmenter/run.py +131 -0
- autorag/nodes/passagecompressor/__init__.py +4 -0
- autorag/nodes/passagecompressor/base.py +78 -0
- autorag/nodes/passagecompressor/longllmlingua.py +115 -0
- autorag/nodes/passagecompressor/pass_compressor.py +16 -0
- autorag/nodes/passagecompressor/refine.py +54 -0
- autorag/nodes/passagecompressor/run.py +186 -0
- autorag/nodes/passagecompressor/tree_summarize.py +56 -0
- autorag/nodes/passagefilter/__init__.py +6 -0
- autorag/nodes/passagefilter/base.py +40 -0
- autorag/nodes/passagefilter/pass_passage_filter.py +14 -0
- autorag/nodes/passagefilter/percentile_cutoff.py +58 -0
- autorag/nodes/passagefilter/recency.py +105 -0
- autorag/nodes/passagefilter/run.py +138 -0
- autorag/nodes/passagefilter/similarity_percentile_cutoff.py +134 -0
- autorag/nodes/passagefilter/similarity_threshold_cutoff.py +112 -0
- autorag/nodes/passagefilter/threshold_cutoff.py +78 -0
- autorag/nodes/passagereranker/__init__.py +16 -0
- autorag/nodes/passagereranker/base.py +44 -0
- autorag/nodes/passagereranker/cohere.py +118 -0
- autorag/nodes/passagereranker/colbert.py +213 -0
- autorag/nodes/passagereranker/flag_embedding.py +112 -0
- autorag/nodes/passagereranker/flag_embedding_llm.py +101 -0
- autorag/nodes/passagereranker/flashrank.py +245 -0
- autorag/nodes/passagereranker/jina.py +115 -0
- autorag/nodes/passagereranker/koreranker.py +136 -0
- autorag/nodes/passagereranker/mixedbreadai.py +126 -0
- autorag/nodes/passagereranker/monot5.py +190 -0
- autorag/nodes/passagereranker/openvino.py +191 -0
- autorag/nodes/passagereranker/pass_reranker.py +31 -0
- autorag/nodes/passagereranker/rankgpt.py +170 -0
- autorag/nodes/passagereranker/run.py +145 -0
- autorag/nodes/passagereranker/sentence_transformer.py +129 -0
- autorag/nodes/passagereranker/tart/__init__.py +1 -0
- autorag/nodes/passagereranker/tart/modeling_enc_t5.py +152 -0
- autorag/nodes/passagereranker/tart/tart.py +139 -0
- autorag/nodes/passagereranker/tart/tokenization_enc_t5.py +112 -0
- autorag/nodes/passagereranker/time_reranker.py +72 -0
- autorag/nodes/passagereranker/upr.py +160 -0
- autorag/nodes/passagereranker/voyageai.py +109 -0
- autorag/nodes/promptmaker/__init__.py +12 -0
- autorag/nodes/promptmaker/base.py +32 -0
- autorag/nodes/promptmaker/chat_fstring.py +73 -0
- autorag/nodes/promptmaker/fstring.py +49 -0
- autorag/nodes/promptmaker/long_context_reorder.py +83 -0
- autorag/nodes/promptmaker/run.py +283 -0
- autorag/nodes/promptmaker/window_replacement.py +85 -0
- autorag/nodes/queryexpansion/__init__.py +4 -0
- autorag/nodes/queryexpansion/base.py +62 -0
- autorag/nodes/queryexpansion/hyde.py +43 -0
- autorag/nodes/queryexpansion/multi_query_expansion.py +57 -0
- autorag/nodes/queryexpansion/pass_query_expansion.py +22 -0
- autorag/nodes/queryexpansion/query_decompose.py +111 -0
- autorag/nodes/queryexpansion/run.py +308 -0
- autorag/nodes/retrieval/__init__.py +0 -0
- autorag/nodes/retrieval/base.py +127 -0
- autorag/nodes/retrieval/run_util.py +152 -0
- autorag/nodes/semanticretrieval/__init__.py +1 -0
- autorag/nodes/semanticretrieval/run.py +148 -0
- autorag/nodes/semanticretrieval/vectordb.py +339 -0
- autorag/nodes/util.py +16 -0
- autorag/parser.py +37 -0
- autorag/schema/__init__.py +3 -0
- autorag/schema/base.py +35 -0
- autorag/schema/metricinput.py +99 -0
- autorag/schema/module.py +24 -0
- autorag/schema/node.py +144 -0
- autorag/strategy.py +165 -0
- autorag/support.py +235 -0
- autorag/utils/__init__.py +8 -0
- autorag/utils/cast.py +45 -0
- autorag/utils/preprocess.py +149 -0
- autorag/utils/util.py +759 -0
- autorag/validator.py +98 -0
- autorag/vectordb/__init__.py +75 -0
- autorag/vectordb/base.py +73 -0
- autorag/vectordb/chroma.py +118 -0
- autorag/vectordb/couchbase.py +239 -0
- autorag/vectordb/milvus.py +169 -0
- autorag/vectordb/pinecone.py +121 -0
- autorag/vectordb/qdrant.py +155 -0
- autorag/vectordb/weaviate.py +184 -0
- autorag/web.py +81 -0
- autorag-0.0.0.dist-info/METADATA +780 -0
- autorag-0.0.0.dist-info/RECORD +184 -0
- autorag-0.0.0.dist-info/WHEEL +5 -0
- autorag-0.0.0.dist-info/entry_points.txt +2 -0
- autorag-0.0.0.dist-info/licenses/LICENSE +201 -0
- autorag-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, List, Tuple
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from autorag.nodes.passagereranker.base import BasePassageReranker
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from autorag.utils.util import (
|
|
11
|
+
make_batch,
|
|
12
|
+
sort_by_scores,
|
|
13
|
+
flatten_apply,
|
|
14
|
+
select_top_k,
|
|
15
|
+
result_to_dataframe,
|
|
16
|
+
pop_params,
|
|
17
|
+
empty_cuda_cache,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class OpenVINOReranker(BasePassageReranker):
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
project_dir: str,
|
|
25
|
+
model: str = "BAAI/bge-reranker-large",
|
|
26
|
+
*args,
|
|
27
|
+
**kwargs,
|
|
28
|
+
):
|
|
29
|
+
super().__init__(project_dir)
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
from huggingface_hub import HfApi
|
|
33
|
+
from transformers import AutoTokenizer
|
|
34
|
+
except ImportError as e:
|
|
35
|
+
raise ValueError(
|
|
36
|
+
"Could not import huggingface_hub python package. "
|
|
37
|
+
"Please install it with: "
|
|
38
|
+
"`pip install -U huggingface_hub`."
|
|
39
|
+
) from e
|
|
40
|
+
|
|
41
|
+
def require_model_export(
|
|
42
|
+
model_id: str, revision: Any = None, subfolder: Any = None
|
|
43
|
+
) -> bool:
|
|
44
|
+
model_dir = Path(model_id)
|
|
45
|
+
if subfolder is not None:
|
|
46
|
+
model_dir = model_dir / subfolder
|
|
47
|
+
if model_dir.is_dir():
|
|
48
|
+
return (
|
|
49
|
+
not (model_dir / "openvino_model.xml").exists()
|
|
50
|
+
or not (model_dir / "openvino_model.bin").exists()
|
|
51
|
+
)
|
|
52
|
+
hf_api = HfApi()
|
|
53
|
+
try:
|
|
54
|
+
model_info = hf_api.model_info(model_id, revision=revision or "main")
|
|
55
|
+
normalized_subfolder = (
|
|
56
|
+
None if subfolder is None else Path(subfolder).as_posix()
|
|
57
|
+
)
|
|
58
|
+
model_files = [
|
|
59
|
+
file.rfilename
|
|
60
|
+
for file in model_info.siblings
|
|
61
|
+
if normalized_subfolder is None
|
|
62
|
+
or file.rfilename.startswith(normalized_subfolder)
|
|
63
|
+
]
|
|
64
|
+
ov_model_path = (
|
|
65
|
+
"openvino_model.xml"
|
|
66
|
+
if subfolder is None
|
|
67
|
+
else f"{normalized_subfolder}/openvino_model.xml"
|
|
68
|
+
)
|
|
69
|
+
return (
|
|
70
|
+
ov_model_path not in model_files
|
|
71
|
+
or ov_model_path.replace(".xml", ".bin") not in model_files
|
|
72
|
+
)
|
|
73
|
+
except Exception:
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
from optimum.intel.openvino import OVModelForSequenceClassification
|
|
78
|
+
except ImportError:
|
|
79
|
+
raise ImportError(
|
|
80
|
+
"Please install optimum package to use OpenVINOReranker"
|
|
81
|
+
"pip install 'optimum[openvino,nncf]'"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
model_kwargs = pop_params(
|
|
85
|
+
OVModelForSequenceClassification.from_pretrained, kwargs
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if require_model_export(model):
|
|
89
|
+
# use remote model
|
|
90
|
+
self.model = OVModelForSequenceClassification.from_pretrained(
|
|
91
|
+
model, export=True, **model_kwargs
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
# use local model
|
|
95
|
+
self.model = OVModelForSequenceClassification.from_pretrained(
|
|
96
|
+
model, **model_kwargs
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
self.tokenizer = AutoTokenizer.from_pretrained(model)
|
|
100
|
+
|
|
101
|
+
def __del__(self):
|
|
102
|
+
del self.model
|
|
103
|
+
del self.tokenizer
|
|
104
|
+
empty_cuda_cache()
|
|
105
|
+
super().__del__()
|
|
106
|
+
|
|
107
|
+
@result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
|
|
108
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
109
|
+
queries, contents, _, ids = self.cast_to_run(previous_result)
|
|
110
|
+
top_k = kwargs.get("top_k", 3)
|
|
111
|
+
batch = kwargs.get("batch", 64)
|
|
112
|
+
return self._pure(queries, contents, ids, top_k, batch)
|
|
113
|
+
|
|
114
|
+
def _pure(
|
|
115
|
+
self,
|
|
116
|
+
queries: List[str],
|
|
117
|
+
contents_list: List[List[str]],
|
|
118
|
+
ids_list: List[List[str]],
|
|
119
|
+
top_k: int,
|
|
120
|
+
batch: int = 64,
|
|
121
|
+
) -> Tuple[List[List[str]], List[List[str]], List[List[float]]]:
|
|
122
|
+
"""
|
|
123
|
+
Rerank a list of contents based on their relevance to a query using MonoT5.
|
|
124
|
+
|
|
125
|
+
:param queries: The list of queries to use for reranking
|
|
126
|
+
:param contents_list: The list of lists of contents to rerank
|
|
127
|
+
:param ids_list: The list of lists of ids retrieved from the initial ranking
|
|
128
|
+
:param top_k: The number of passages to be retrieved
|
|
129
|
+
|
|
130
|
+
:param batch: The number of queries to be processed in a batch
|
|
131
|
+
:return: tuple of lists containing the reranked contents, ids, and scores
|
|
132
|
+
"""
|
|
133
|
+
# Retrieve the tokens used by the model to represent false and true predictions
|
|
134
|
+
|
|
135
|
+
nested_list = [
|
|
136
|
+
list(map(lambda x: [query, x], content_list))
|
|
137
|
+
for query, content_list in zip(queries, contents_list)
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
rerank_scores = flatten_apply(
|
|
141
|
+
openvino_run_model,
|
|
142
|
+
nested_list,
|
|
143
|
+
model=self.model,
|
|
144
|
+
batch_size=batch,
|
|
145
|
+
tokenizer=self.tokenizer,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
df = pd.DataFrame(
|
|
149
|
+
{
|
|
150
|
+
"contents": contents_list,
|
|
151
|
+
"ids": ids_list,
|
|
152
|
+
"scores": rerank_scores,
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
df[["contents", "ids", "scores"]] = df.apply(
|
|
156
|
+
sort_by_scores, axis=1, result_type="expand"
|
|
157
|
+
)
|
|
158
|
+
results = select_top_k(df, ["contents", "ids", "scores"], top_k)
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
results["contents"].tolist(),
|
|
162
|
+
results["ids"].tolist(),
|
|
163
|
+
results["scores"].tolist(),
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def openvino_run_model(
|
|
168
|
+
input_texts,
|
|
169
|
+
model,
|
|
170
|
+
batch_size: int,
|
|
171
|
+
tokenizer,
|
|
172
|
+
):
|
|
173
|
+
batch_input_texts = make_batch(input_texts, batch_size)
|
|
174
|
+
results = []
|
|
175
|
+
for batch_texts in batch_input_texts:
|
|
176
|
+
input_tensors = tokenizer(
|
|
177
|
+
batch_texts,
|
|
178
|
+
padding=True,
|
|
179
|
+
truncation=True,
|
|
180
|
+
return_tensors="pt",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
outputs = model(**input_tensors, return_dict=True)
|
|
184
|
+
if outputs[0].shape[1] > 1:
|
|
185
|
+
scores = outputs[0][:, 1]
|
|
186
|
+
else:
|
|
187
|
+
scores = outputs[0].flatten()
|
|
188
|
+
|
|
189
|
+
scores = list(map(float, (1 / (1 + np.exp(-np.array(scores))))))
|
|
190
|
+
results.extend(scores)
|
|
191
|
+
return results
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from autorag.nodes.passagereranker.base import BasePassageReranker
|
|
6
|
+
from autorag.utils import result_to_dataframe
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PassReranker(BasePassageReranker):
|
|
10
|
+
@result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
|
|
11
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
12
|
+
top_k = kwargs.pop("top_k")
|
|
13
|
+
|
|
14
|
+
_, contents_list, scores_list, ids_list = self.cast_to_run(previous_result)
|
|
15
|
+
return self._pure(contents_list, scores_list, ids_list, top_k)
|
|
16
|
+
|
|
17
|
+
def _pure(
|
|
18
|
+
self,
|
|
19
|
+
contents_list: List[List[str]],
|
|
20
|
+
scores_list: List[List[float]],
|
|
21
|
+
ids_list: List[List[str]],
|
|
22
|
+
top_k: int,
|
|
23
|
+
):
|
|
24
|
+
"""
|
|
25
|
+
Do not perform reranking.
|
|
26
|
+
Return the given top-k passages as is.
|
|
27
|
+
"""
|
|
28
|
+
contents_list = list(map(lambda x: x[:top_k], contents_list))
|
|
29
|
+
scores_list = list(map(lambda x: x[:top_k], scores_list))
|
|
30
|
+
ids_list = list(map(lambda x: x[:top_k], ids_list))
|
|
31
|
+
return contents_list, ids_list, scores_list
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
from typing import List, Optional, Sequence, Tuple, Union
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from llama_index.core.base.llms.types import ChatMessage, ChatResponse
|
|
6
|
+
from llama_index.core.llms import LLM
|
|
7
|
+
from llama_index.core.postprocessor.rankGPT_rerank import RankGPTRerank
|
|
8
|
+
from llama_index.core.schema import NodeWithScore, QueryBundle, TextNode
|
|
9
|
+
from llama_index.core.utils import print_text
|
|
10
|
+
from llama_index.llms.openai import OpenAI
|
|
11
|
+
|
|
12
|
+
from autorag import generator_models
|
|
13
|
+
from autorag.nodes.passagereranker.base import BasePassageReranker
|
|
14
|
+
from autorag.utils.util import (
|
|
15
|
+
get_event_loop,
|
|
16
|
+
process_batch,
|
|
17
|
+
pop_params,
|
|
18
|
+
result_to_dataframe,
|
|
19
|
+
empty_cuda_cache,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class RankGPT(BasePassageReranker):
|
|
24
|
+
def __init__(
|
|
25
|
+
self, project_dir: str, llm: Optional[Union[str, LLM]] = None, **kwargs
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Initialize the RankGPT reranker.
|
|
29
|
+
|
|
30
|
+
:param project_dir: The project directory
|
|
31
|
+
:param llm: The LLM model to use for RankGPT rerank.
|
|
32
|
+
It is a llama index model.
|
|
33
|
+
Default is the OpenAI model with gpt-4o-mini.
|
|
34
|
+
:param kwargs: The keyword arguments for the LLM model.
|
|
35
|
+
"""
|
|
36
|
+
super().__init__(project_dir)
|
|
37
|
+
if llm is None:
|
|
38
|
+
self.llm = OpenAI(model="gpt-4o-mini")
|
|
39
|
+
else:
|
|
40
|
+
if not isinstance(llm, LLM):
|
|
41
|
+
llm_class = generator_models[llm]
|
|
42
|
+
llm_param = pop_params(llm_class.__init__, kwargs)
|
|
43
|
+
self.llm = llm_class(**llm_param)
|
|
44
|
+
else:
|
|
45
|
+
self.llm = llm
|
|
46
|
+
|
|
47
|
+
def __del__(self):
|
|
48
|
+
del self.llm
|
|
49
|
+
empty_cuda_cache()
|
|
50
|
+
super().__del__()
|
|
51
|
+
|
|
52
|
+
@result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
|
|
53
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
54
|
+
queries, contents, scores, ids = self.cast_to_run(previous_result)
|
|
55
|
+
top_k = kwargs.get("top_k", 1)
|
|
56
|
+
verbose = kwargs.get("verbose", False)
|
|
57
|
+
rankgpt_rerank_prompt = kwargs.get("rankgpt_rerank_prompt", None)
|
|
58
|
+
batch = kwargs.get("batch", 16)
|
|
59
|
+
return self._pure(
|
|
60
|
+
queries=queries,
|
|
61
|
+
contents_list=contents,
|
|
62
|
+
scores_list=scores,
|
|
63
|
+
ids_list=ids,
|
|
64
|
+
top_k=top_k,
|
|
65
|
+
verbose=verbose,
|
|
66
|
+
rankgpt_rerank_prompt=rankgpt_rerank_prompt,
|
|
67
|
+
batch=batch,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def _pure(
|
|
71
|
+
self,
|
|
72
|
+
queries: List[str],
|
|
73
|
+
contents_list: List[List[str]],
|
|
74
|
+
scores_list: List[List[float]],
|
|
75
|
+
ids_list: List[List[str]],
|
|
76
|
+
top_k: int,
|
|
77
|
+
verbose: bool = False,
|
|
78
|
+
rankgpt_rerank_prompt: Optional[str] = None,
|
|
79
|
+
batch: int = 16,
|
|
80
|
+
) -> Tuple[List[List[str]], List[List[str]], List[List[float]]]:
|
|
81
|
+
"""
|
|
82
|
+
Rerank given context paragraphs using RankGPT.
|
|
83
|
+
Return pseudo scores, since the actual scores are not available on RankGPT.
|
|
84
|
+
|
|
85
|
+
:param queries: The list of queries to use for reranking
|
|
86
|
+
:param contents_list: The list of lists of contents to rerank
|
|
87
|
+
:param scores_list: The list of lists of scores retrieved from the initial ranking
|
|
88
|
+
:param ids_list: The list of lists of ids retrieved from the initial ranking
|
|
89
|
+
:param top_k: The number of passages to be retrieved
|
|
90
|
+
:param verbose: Whether to print intermediate steps.
|
|
91
|
+
:param rankgpt_rerank_prompt: The prompt template for RankGPT rerank.
|
|
92
|
+
Default is RankGPT's default prompt.
|
|
93
|
+
:param batch: The number of queries to be processed in a batch.
|
|
94
|
+
:return: Tuple of lists containing the reranked contents, ids, and scores
|
|
95
|
+
"""
|
|
96
|
+
query_bundles = list(map(lambda query: QueryBundle(query_str=query), queries))
|
|
97
|
+
nodes_list = [
|
|
98
|
+
list(
|
|
99
|
+
map(
|
|
100
|
+
lambda x: NodeWithScore(node=TextNode(text=x[0]), score=x[1]),
|
|
101
|
+
zip(content_list, score_list),
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
for content_list, score_list in zip(contents_list, scores_list)
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
reranker = AsyncRankGPTRerank(
|
|
108
|
+
top_n=top_k,
|
|
109
|
+
llm=self.llm,
|
|
110
|
+
verbose=verbose,
|
|
111
|
+
rankgpt_rerank_prompt=rankgpt_rerank_prompt,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
tasks = [
|
|
115
|
+
reranker.async_postprocess_nodes(nodes, query, ids)
|
|
116
|
+
for nodes, query, ids in zip(nodes_list, query_bundles, ids_list)
|
|
117
|
+
]
|
|
118
|
+
loop = get_event_loop()
|
|
119
|
+
rerank_result = loop.run_until_complete(process_batch(tasks, batch_size=batch))
|
|
120
|
+
content_result = [
|
|
121
|
+
list(map(lambda x: x.node.text, res[0])) for res in rerank_result
|
|
122
|
+
]
|
|
123
|
+
score_result = [
|
|
124
|
+
np.linspace(1.0, 0.0, len(res[0])).tolist() for res in rerank_result
|
|
125
|
+
]
|
|
126
|
+
id_result = [res[1] for res in rerank_result]
|
|
127
|
+
|
|
128
|
+
del reranker
|
|
129
|
+
|
|
130
|
+
return content_result, id_result, score_result
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class AsyncRankGPTRerank(RankGPTRerank):
|
|
134
|
+
async def async_run_llm(self, messages: Sequence[ChatMessage]) -> ChatResponse:
|
|
135
|
+
return await self.llm.achat(messages)
|
|
136
|
+
|
|
137
|
+
async def async_postprocess_nodes(
|
|
138
|
+
self,
|
|
139
|
+
nodes: List[NodeWithScore],
|
|
140
|
+
query_bundle: QueryBundle,
|
|
141
|
+
ids: Optional[List[str]] = None,
|
|
142
|
+
) -> Tuple[List[NodeWithScore], List[str]]:
|
|
143
|
+
if ids is None:
|
|
144
|
+
ids = [str(i) for i in range(len(nodes))]
|
|
145
|
+
|
|
146
|
+
items = {
|
|
147
|
+
"query": query_bundle.query_str,
|
|
148
|
+
"hits": [{"content": node.get_content()} for node in nodes],
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
messages = self.create_permutation_instruction(item=items)
|
|
152
|
+
permutation = await self.async_run_llm(messages=messages)
|
|
153
|
+
if permutation.message is not None and permutation.message.content is not None:
|
|
154
|
+
rerank_ranks = self._receive_permutation(
|
|
155
|
+
items, str(permutation.message.content)
|
|
156
|
+
)
|
|
157
|
+
if self.verbose:
|
|
158
|
+
print_text(f"After Reranking, new rank list for nodes: {rerank_ranks}")
|
|
159
|
+
|
|
160
|
+
initial_results: List[NodeWithScore] = []
|
|
161
|
+
id_results = []
|
|
162
|
+
|
|
163
|
+
for idx in rerank_ranks:
|
|
164
|
+
initial_results.append(
|
|
165
|
+
NodeWithScore(node=nodes[idx].node, score=nodes[idx].score)
|
|
166
|
+
)
|
|
167
|
+
id_results.append(ids[idx])
|
|
168
|
+
return initial_results[: self.top_n], id_results[: self.top_n]
|
|
169
|
+
else:
|
|
170
|
+
return nodes[: self.top_n], ids[: self.top_n]
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import pathlib
|
|
4
|
+
from typing import List, Dict
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
from autorag.nodes.retrieval.run_util import evaluate_retrieval_node
|
|
9
|
+
from autorag.schema.metricinput import MetricInput
|
|
10
|
+
from autorag.strategy import measure_speed, filter_by_threshold, select_best
|
|
11
|
+
from autorag.utils.util import apply_recursive, to_list
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger("AutoRAG")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run_passage_reranker_node(
|
|
17
|
+
modules: List,
|
|
18
|
+
module_params: List[Dict],
|
|
19
|
+
previous_result: pd.DataFrame,
|
|
20
|
+
node_line_dir: str,
|
|
21
|
+
strategies: Dict,
|
|
22
|
+
) -> pd.DataFrame:
|
|
23
|
+
"""
|
|
24
|
+
Run evaluation and select the best module among passage reranker node results.
|
|
25
|
+
|
|
26
|
+
:param modules: Passage reranker modules to run.
|
|
27
|
+
:param module_params: Passage reranker module parameters.
|
|
28
|
+
:param previous_result: Previous result dataframe.
|
|
29
|
+
Could be retrieval, reranker modules result.
|
|
30
|
+
It means it must contain 'query', 'retrieved_contents', 'retrieved_ids', 'retrieve_scores' columns.
|
|
31
|
+
:param node_line_dir: This node line's directory.
|
|
32
|
+
:param strategies: Strategies for passage reranker node.
|
|
33
|
+
In this node, we use 'retrieval_f1', 'retrieval_recall' and 'retrieval_precision'.
|
|
34
|
+
You can skip evaluation when you use only one module and a module parameter.
|
|
35
|
+
:return: The best result dataframe with previous result columns.
|
|
36
|
+
"""
|
|
37
|
+
if not os.path.exists(node_line_dir):
|
|
38
|
+
os.makedirs(node_line_dir)
|
|
39
|
+
project_dir = pathlib.PurePath(node_line_dir).parent.parent
|
|
40
|
+
qa_df = pd.read_parquet(
|
|
41
|
+
os.path.join(project_dir, "data", "qa.parquet"), engine="pyarrow"
|
|
42
|
+
)
|
|
43
|
+
retrieval_gt = qa_df["retrieval_gt"].tolist()
|
|
44
|
+
retrieval_gt = apply_recursive(lambda x: str(x), to_list(retrieval_gt))
|
|
45
|
+
|
|
46
|
+
# make rows to metric_inputs
|
|
47
|
+
metric_inputs = [
|
|
48
|
+
MetricInput(retrieval_gt=ret_gt, query=query, generation_gt=gen_gt)
|
|
49
|
+
for ret_gt, query, gen_gt in zip(
|
|
50
|
+
retrieval_gt, qa_df["query"].tolist(), qa_df["generation_gt"].tolist()
|
|
51
|
+
)
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
results, execution_times = zip(
|
|
55
|
+
*map(
|
|
56
|
+
lambda task: measure_speed(
|
|
57
|
+
task[0].run_evaluator,
|
|
58
|
+
project_dir=project_dir,
|
|
59
|
+
previous_result=previous_result,
|
|
60
|
+
**task[1],
|
|
61
|
+
),
|
|
62
|
+
zip(modules, module_params),
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
average_times = list(map(lambda x: x / len(results[0]), execution_times))
|
|
66
|
+
|
|
67
|
+
# run metrics before filtering
|
|
68
|
+
if strategies.get("metrics") is None:
|
|
69
|
+
raise ValueError(
|
|
70
|
+
"You must at least one metrics for passage_reranker evaluation."
|
|
71
|
+
)
|
|
72
|
+
results = list(
|
|
73
|
+
map(
|
|
74
|
+
lambda x: evaluate_retrieval_node(
|
|
75
|
+
x,
|
|
76
|
+
metric_inputs,
|
|
77
|
+
strategies.get("metrics"),
|
|
78
|
+
),
|
|
79
|
+
results,
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# save results to folder
|
|
84
|
+
save_dir = os.path.join(node_line_dir, "passage_reranker") # node name
|
|
85
|
+
if not os.path.exists(save_dir):
|
|
86
|
+
os.makedirs(save_dir)
|
|
87
|
+
filepaths = list(
|
|
88
|
+
map(lambda x: os.path.join(save_dir, f"{x}.parquet"), range(len(modules)))
|
|
89
|
+
)
|
|
90
|
+
list(
|
|
91
|
+
map(lambda x: x[0].to_parquet(x[1], index=False), zip(results, filepaths))
|
|
92
|
+
) # execute save to parquet
|
|
93
|
+
filenames = list(map(lambda x: os.path.basename(x), filepaths))
|
|
94
|
+
|
|
95
|
+
summary_df = pd.DataFrame(
|
|
96
|
+
{
|
|
97
|
+
"filename": filenames,
|
|
98
|
+
"module_name": list(map(lambda module: module.__name__, modules)),
|
|
99
|
+
"module_params": module_params,
|
|
100
|
+
"execution_time": average_times,
|
|
101
|
+
**{
|
|
102
|
+
f"passage_reranker_{metric}": list(
|
|
103
|
+
map(lambda result: result[metric].mean(), results)
|
|
104
|
+
)
|
|
105
|
+
for metric in strategies.get("metrics")
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# filter by strategies
|
|
111
|
+
if strategies.get("speed_threshold") is not None:
|
|
112
|
+
results, filenames = filter_by_threshold(
|
|
113
|
+
results, average_times, strategies["speed_threshold"], filenames
|
|
114
|
+
)
|
|
115
|
+
selected_result, selected_filename = select_best(
|
|
116
|
+
results,
|
|
117
|
+
strategies.get("metrics"),
|
|
118
|
+
filenames,
|
|
119
|
+
strategies.get("strategy", "mean"),
|
|
120
|
+
)
|
|
121
|
+
# change metric name columns to passage_reranker_metric_name
|
|
122
|
+
selected_result = selected_result.rename(
|
|
123
|
+
columns={
|
|
124
|
+
metric_name: f"passage_reranker_{metric_name}"
|
|
125
|
+
for metric_name in strategies["metrics"]
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
# drop retrieval result columns in previous_result
|
|
129
|
+
previous_result = previous_result.drop(
|
|
130
|
+
columns=["retrieved_contents", "retrieved_ids", "retrieve_scores"]
|
|
131
|
+
)
|
|
132
|
+
best_result = pd.concat([previous_result, selected_result], axis=1)
|
|
133
|
+
|
|
134
|
+
# add 'is_best' column to summary file
|
|
135
|
+
summary_df["is_best"] = summary_df["filename"] == selected_filename
|
|
136
|
+
|
|
137
|
+
# save files
|
|
138
|
+
summary_df.to_csv(os.path.join(save_dir, "summary.csv"), index=False)
|
|
139
|
+
best_result.to_parquet(
|
|
140
|
+
os.path.join(
|
|
141
|
+
save_dir, f"best_{os.path.splitext(selected_filename)[0]}.parquet"
|
|
142
|
+
),
|
|
143
|
+
index=False,
|
|
144
|
+
)
|
|
145
|
+
return best_result
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from typing import List, Tuple
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from autorag.nodes.passagereranker.base import BasePassageReranker
|
|
6
|
+
from autorag.utils.util import (
|
|
7
|
+
flatten_apply,
|
|
8
|
+
make_batch,
|
|
9
|
+
select_top_k,
|
|
10
|
+
sort_by_scores,
|
|
11
|
+
pop_params,
|
|
12
|
+
result_to_dataframe,
|
|
13
|
+
empty_cuda_cache,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SentenceTransformerReranker(BasePassageReranker):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
project_dir: str,
|
|
21
|
+
model_name: str = "cross-encoder/ms-marco-MiniLM-L-2-v2",
|
|
22
|
+
*args,
|
|
23
|
+
**kwargs,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Initialize the Sentence Transformer reranker node.
|
|
27
|
+
|
|
28
|
+
:param project_dir: The project directory
|
|
29
|
+
:param model_name: The name of the Sentence Transformer model to use for reranking
|
|
30
|
+
Default is "cross-encoder/ms-marco-MiniLM-L-2-v2"
|
|
31
|
+
:param kwargs: The CrossEncoder parameters
|
|
32
|
+
"""
|
|
33
|
+
super().__init__(project_dir, *args, **kwargs)
|
|
34
|
+
try:
|
|
35
|
+
import torch
|
|
36
|
+
from sentence_transformers import CrossEncoder
|
|
37
|
+
except ImportError:
|
|
38
|
+
raise ImportError(
|
|
39
|
+
"You have to install AutoRAG[gpu] to use SentenceTransformerReranker"
|
|
40
|
+
)
|
|
41
|
+
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
42
|
+
model_params = pop_params(CrossEncoder.__init__, kwargs)
|
|
43
|
+
self.model = CrossEncoder(model_name, device=self.device, **model_params)
|
|
44
|
+
|
|
45
|
+
def __del__(self):
|
|
46
|
+
del self.model
|
|
47
|
+
empty_cuda_cache()
|
|
48
|
+
super().__del__()
|
|
49
|
+
|
|
50
|
+
@result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
|
|
51
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
52
|
+
"""
|
|
53
|
+
Rerank a list of contents based on their relevance to a query using a Sentence Transformer model.
|
|
54
|
+
|
|
55
|
+
:param previous_result: The previous result
|
|
56
|
+
:param top_k: The number of passages to be retrieved
|
|
57
|
+
:param batch: The number of queries to be processed in a batch
|
|
58
|
+
:return: pd DataFrame containing the reranked contents, ids, and scores
|
|
59
|
+
"""
|
|
60
|
+
queries, contents_list, scores_list, ids_list = self.cast_to_run(
|
|
61
|
+
previous_result
|
|
62
|
+
)
|
|
63
|
+
top_k = kwargs.get("top_k", 1)
|
|
64
|
+
batch = kwargs.get("batch", 64)
|
|
65
|
+
return self._pure(queries, contents_list, ids_list, top_k, batch)
|
|
66
|
+
|
|
67
|
+
def _pure(
|
|
68
|
+
self,
|
|
69
|
+
queries: List[str],
|
|
70
|
+
contents_list: List[List[str]],
|
|
71
|
+
ids_list: List[List[str]],
|
|
72
|
+
top_k: int,
|
|
73
|
+
batch: int = 64,
|
|
74
|
+
) -> Tuple[List[List[str]], List[List[str]], List[List[float]]]:
|
|
75
|
+
"""
|
|
76
|
+
Rerank a list of contents based on their relevance to a query using a Sentence Transformer model.
|
|
77
|
+
|
|
78
|
+
:param queries: The list of queries to use for reranking
|
|
79
|
+
:param contents_list: The list of lists of contents to rerank
|
|
80
|
+
:param ids_list: The list of lists of ids retrieved from the initial ranking
|
|
81
|
+
:param top_k: The number of passages to be retrieved
|
|
82
|
+
:param batch: The number of queries to be processed in a batch
|
|
83
|
+
|
|
84
|
+
:return: tuple of lists containing the reranked contents, ids, and scores
|
|
85
|
+
"""
|
|
86
|
+
nested_list = [
|
|
87
|
+
list(map(lambda x: [query, x], content_list))
|
|
88
|
+
for query, content_list in zip(queries, contents_list)
|
|
89
|
+
]
|
|
90
|
+
rerank_scores = flatten_apply(
|
|
91
|
+
sentence_transformer_run_model,
|
|
92
|
+
nested_list,
|
|
93
|
+
model=self.model,
|
|
94
|
+
batch_size=batch,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
df = pd.DataFrame(
|
|
98
|
+
{
|
|
99
|
+
"contents": contents_list,
|
|
100
|
+
"ids": ids_list,
|
|
101
|
+
"scores": rerank_scores,
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
df[["contents", "ids", "scores"]] = df.apply(
|
|
105
|
+
sort_by_scores, axis=1, result_type="expand"
|
|
106
|
+
)
|
|
107
|
+
results = select_top_k(df, ["contents", "ids", "scores"], top_k)
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
results["contents"].tolist(),
|
|
111
|
+
results["ids"].tolist(),
|
|
112
|
+
results["scores"].tolist(),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def sentence_transformer_run_model(input_texts, model, batch_size: int):
|
|
117
|
+
try:
|
|
118
|
+
import torch
|
|
119
|
+
except ImportError:
|
|
120
|
+
raise ImportError(
|
|
121
|
+
"You have to install AutoRAG[gpu] to use SentenceTransformerReranker"
|
|
122
|
+
)
|
|
123
|
+
batch_input_texts = make_batch(input_texts, batch_size)
|
|
124
|
+
results = []
|
|
125
|
+
for batch_texts in batch_input_texts:
|
|
126
|
+
with torch.no_grad():
|
|
127
|
+
pred_scores = model.predict(batch_texts)
|
|
128
|
+
results.extend(pred_scores.tolist())
|
|
129
|
+
return results
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .tart import Tart
|