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.
Files changed (184) hide show
  1. autorag/__init__.py +82 -0
  2. autorag/chunker.py +51 -0
  3. autorag/cli.py +209 -0
  4. autorag/dashboard.py +199 -0
  5. autorag/data/__init__.py +109 -0
  6. autorag/data/chunk/__init__.py +2 -0
  7. autorag/data/chunk/base.py +128 -0
  8. autorag/data/chunk/langchain_chunk.py +76 -0
  9. autorag/data/chunk/llama_index_chunk.py +96 -0
  10. autorag/data/chunk/run.py +38 -0
  11. autorag/data/legacy/__init__.py +0 -0
  12. autorag/data/legacy/corpus/__init__.py +2 -0
  13. autorag/data/legacy/corpus/langchain.py +47 -0
  14. autorag/data/legacy/corpus/llama_index.py +93 -0
  15. autorag/data/legacy/qacreation/__init__.py +6 -0
  16. autorag/data/legacy/qacreation/base.py +239 -0
  17. autorag/data/legacy/qacreation/llama_index.py +253 -0
  18. autorag/data/legacy/qacreation/llama_index_default_prompt.txt +54 -0
  19. autorag/data/legacy/qacreation/ragas.py +75 -0
  20. autorag/data/legacy/qacreation/simple.py +99 -0
  21. autorag/data/parse/__init__.py +1 -0
  22. autorag/data/parse/base.py +79 -0
  23. autorag/data/parse/clova.py +194 -0
  24. autorag/data/parse/langchain_parse.py +87 -0
  25. autorag/data/parse/llamaparse.py +126 -0
  26. autorag/data/parse/run.py +141 -0
  27. autorag/data/parse/table_hybrid_parse.py +134 -0
  28. autorag/data/qa/__init__.py +3 -0
  29. autorag/data/qa/evolve/__init__.py +0 -0
  30. autorag/data/qa/evolve/llama_index_query_evolve.py +64 -0
  31. autorag/data/qa/evolve/openai_query_evolve.py +81 -0
  32. autorag/data/qa/evolve/prompt.py +288 -0
  33. autorag/data/qa/extract_evidence.py +1 -0
  34. autorag/data/qa/filter/__init__.py +0 -0
  35. autorag/data/qa/filter/dontknow.py +117 -0
  36. autorag/data/qa/filter/passage_dependency.py +88 -0
  37. autorag/data/qa/filter/prompt.py +73 -0
  38. autorag/data/qa/generation_gt/__init__.py +0 -0
  39. autorag/data/qa/generation_gt/base.py +16 -0
  40. autorag/data/qa/generation_gt/llama_index_gen_gt.py +41 -0
  41. autorag/data/qa/generation_gt/openai_gen_gt.py +84 -0
  42. autorag/data/qa/generation_gt/prompt.py +27 -0
  43. autorag/data/qa/query/__init__.py +0 -0
  44. autorag/data/qa/query/llama_gen_query.py +82 -0
  45. autorag/data/qa/query/openai_gen_query.py +95 -0
  46. autorag/data/qa/query/prompt.py +201 -0
  47. autorag/data/qa/sample.py +26 -0
  48. autorag/data/qa/schema.py +322 -0
  49. autorag/data/utils/__init__.py +0 -0
  50. autorag/data/utils/util.py +103 -0
  51. autorag/deploy/__init__.py +9 -0
  52. autorag/deploy/api.py +303 -0
  53. autorag/deploy/base.py +235 -0
  54. autorag/deploy/gradio.py +74 -0
  55. autorag/deploy/swagger.yml +202 -0
  56. autorag/embedding/__init__.py +0 -0
  57. autorag/embedding/base.py +144 -0
  58. autorag/embedding/vllm.py +256 -0
  59. autorag/evaluation/__init__.py +3 -0
  60. autorag/evaluation/generation.py +88 -0
  61. autorag/evaluation/metric/__init__.py +22 -0
  62. autorag/evaluation/metric/deepeval_prompt.py +322 -0
  63. autorag/evaluation/metric/g_eval_prompts/coh_detailed.txt +32 -0
  64. autorag/evaluation/metric/g_eval_prompts/con_detailed.txt +33 -0
  65. autorag/evaluation/metric/g_eval_prompts/flu_detailed.txt +26 -0
  66. autorag/evaluation/metric/g_eval_prompts/rel_detailed.txt +33 -0
  67. autorag/evaluation/metric/generation.py +504 -0
  68. autorag/evaluation/metric/retrieval.py +115 -0
  69. autorag/evaluation/metric/retrieval_contents.py +65 -0
  70. autorag/evaluation/metric/util.py +88 -0
  71. autorag/evaluation/retrieval.py +83 -0
  72. autorag/evaluation/retrieval_contents.py +65 -0
  73. autorag/evaluation/util.py +43 -0
  74. autorag/evaluator.py +559 -0
  75. autorag/node_line.py +65 -0
  76. autorag/nodes/__init__.py +0 -0
  77. autorag/nodes/generator/__init__.py +4 -0
  78. autorag/nodes/generator/base.py +103 -0
  79. autorag/nodes/generator/llama_index_llm.py +169 -0
  80. autorag/nodes/generator/openai_llm.py +329 -0
  81. autorag/nodes/generator/run.py +148 -0
  82. autorag/nodes/generator/vllm.py +147 -0
  83. autorag/nodes/generator/vllm_api.py +191 -0
  84. autorag/nodes/hybridretrieval/__init__.py +2 -0
  85. autorag/nodes/hybridretrieval/base.py +58 -0
  86. autorag/nodes/hybridretrieval/hybrid_cc.py +227 -0
  87. autorag/nodes/hybridretrieval/hybrid_rrf.py +149 -0
  88. autorag/nodes/hybridretrieval/run.py +137 -0
  89. autorag/nodes/lexicalretrieval/__init__.py +1 -0
  90. autorag/nodes/lexicalretrieval/bm25.py +381 -0
  91. autorag/nodes/lexicalretrieval/run.py +148 -0
  92. autorag/nodes/passageaugmenter/__init__.py +2 -0
  93. autorag/nodes/passageaugmenter/base.py +76 -0
  94. autorag/nodes/passageaugmenter/pass_passage_augmenter.py +43 -0
  95. autorag/nodes/passageaugmenter/prev_next_augmenter.py +155 -0
  96. autorag/nodes/passageaugmenter/run.py +131 -0
  97. autorag/nodes/passagecompressor/__init__.py +4 -0
  98. autorag/nodes/passagecompressor/base.py +78 -0
  99. autorag/nodes/passagecompressor/longllmlingua.py +115 -0
  100. autorag/nodes/passagecompressor/pass_compressor.py +16 -0
  101. autorag/nodes/passagecompressor/refine.py +54 -0
  102. autorag/nodes/passagecompressor/run.py +186 -0
  103. autorag/nodes/passagecompressor/tree_summarize.py +56 -0
  104. autorag/nodes/passagefilter/__init__.py +6 -0
  105. autorag/nodes/passagefilter/base.py +40 -0
  106. autorag/nodes/passagefilter/pass_passage_filter.py +14 -0
  107. autorag/nodes/passagefilter/percentile_cutoff.py +58 -0
  108. autorag/nodes/passagefilter/recency.py +105 -0
  109. autorag/nodes/passagefilter/run.py +138 -0
  110. autorag/nodes/passagefilter/similarity_percentile_cutoff.py +134 -0
  111. autorag/nodes/passagefilter/similarity_threshold_cutoff.py +112 -0
  112. autorag/nodes/passagefilter/threshold_cutoff.py +78 -0
  113. autorag/nodes/passagereranker/__init__.py +16 -0
  114. autorag/nodes/passagereranker/base.py +44 -0
  115. autorag/nodes/passagereranker/cohere.py +118 -0
  116. autorag/nodes/passagereranker/colbert.py +213 -0
  117. autorag/nodes/passagereranker/flag_embedding.py +112 -0
  118. autorag/nodes/passagereranker/flag_embedding_llm.py +101 -0
  119. autorag/nodes/passagereranker/flashrank.py +245 -0
  120. autorag/nodes/passagereranker/jina.py +115 -0
  121. autorag/nodes/passagereranker/koreranker.py +136 -0
  122. autorag/nodes/passagereranker/mixedbreadai.py +126 -0
  123. autorag/nodes/passagereranker/monot5.py +190 -0
  124. autorag/nodes/passagereranker/openvino.py +191 -0
  125. autorag/nodes/passagereranker/pass_reranker.py +31 -0
  126. autorag/nodes/passagereranker/rankgpt.py +170 -0
  127. autorag/nodes/passagereranker/run.py +145 -0
  128. autorag/nodes/passagereranker/sentence_transformer.py +129 -0
  129. autorag/nodes/passagereranker/tart/__init__.py +1 -0
  130. autorag/nodes/passagereranker/tart/modeling_enc_t5.py +152 -0
  131. autorag/nodes/passagereranker/tart/tart.py +139 -0
  132. autorag/nodes/passagereranker/tart/tokenization_enc_t5.py +112 -0
  133. autorag/nodes/passagereranker/time_reranker.py +72 -0
  134. autorag/nodes/passagereranker/upr.py +160 -0
  135. autorag/nodes/passagereranker/voyageai.py +109 -0
  136. autorag/nodes/promptmaker/__init__.py +12 -0
  137. autorag/nodes/promptmaker/base.py +32 -0
  138. autorag/nodes/promptmaker/chat_fstring.py +73 -0
  139. autorag/nodes/promptmaker/fstring.py +49 -0
  140. autorag/nodes/promptmaker/long_context_reorder.py +83 -0
  141. autorag/nodes/promptmaker/run.py +283 -0
  142. autorag/nodes/promptmaker/window_replacement.py +85 -0
  143. autorag/nodes/queryexpansion/__init__.py +4 -0
  144. autorag/nodes/queryexpansion/base.py +62 -0
  145. autorag/nodes/queryexpansion/hyde.py +43 -0
  146. autorag/nodes/queryexpansion/multi_query_expansion.py +57 -0
  147. autorag/nodes/queryexpansion/pass_query_expansion.py +22 -0
  148. autorag/nodes/queryexpansion/query_decompose.py +111 -0
  149. autorag/nodes/queryexpansion/run.py +308 -0
  150. autorag/nodes/retrieval/__init__.py +0 -0
  151. autorag/nodes/retrieval/base.py +127 -0
  152. autorag/nodes/retrieval/run_util.py +152 -0
  153. autorag/nodes/semanticretrieval/__init__.py +1 -0
  154. autorag/nodes/semanticretrieval/run.py +148 -0
  155. autorag/nodes/semanticretrieval/vectordb.py +339 -0
  156. autorag/nodes/util.py +16 -0
  157. autorag/parser.py +37 -0
  158. autorag/schema/__init__.py +3 -0
  159. autorag/schema/base.py +35 -0
  160. autorag/schema/metricinput.py +99 -0
  161. autorag/schema/module.py +24 -0
  162. autorag/schema/node.py +144 -0
  163. autorag/strategy.py +165 -0
  164. autorag/support.py +235 -0
  165. autorag/utils/__init__.py +8 -0
  166. autorag/utils/cast.py +45 -0
  167. autorag/utils/preprocess.py +149 -0
  168. autorag/utils/util.py +759 -0
  169. autorag/validator.py +98 -0
  170. autorag/vectordb/__init__.py +75 -0
  171. autorag/vectordb/base.py +73 -0
  172. autorag/vectordb/chroma.py +118 -0
  173. autorag/vectordb/couchbase.py +239 -0
  174. autorag/vectordb/milvus.py +169 -0
  175. autorag/vectordb/pinecone.py +121 -0
  176. autorag/vectordb/qdrant.py +155 -0
  177. autorag/vectordb/weaviate.py +184 -0
  178. autorag/web.py +81 -0
  179. autorag-0.0.0.dist-info/METADATA +780 -0
  180. autorag-0.0.0.dist-info/RECORD +184 -0
  181. autorag-0.0.0.dist-info/WHEEL +5 -0
  182. autorag-0.0.0.dist-info/entry_points.txt +2 -0
  183. autorag-0.0.0.dist-info/licenses/LICENSE +201 -0
  184. 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