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,152 @@
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+
3
+ import copy
4
+
5
+ from transformers.modeling_outputs import SequenceClassifierOutput
6
+ from transformers.models.t5.modeling_t5 import T5Config, T5PreTrainedModel, T5Stack
7
+ from transformers.utils.model_parallel_utils import assert_device_map, get_device_map
8
+
9
+ from autorag.utils.util import empty_cuda_cache
10
+
11
+
12
+ class EncT5ForSequenceClassification(T5PreTrainedModel):
13
+ _keys_to_ignore_on_load_missing = [
14
+ r"encoder\.embed_tokens\.weight",
15
+ ]
16
+
17
+ def __init__(self, config: T5Config, dropout=0.1):
18
+ super().__init__(config)
19
+ try:
20
+ from torch import nn
21
+ except ImportError:
22
+ raise ImportError("Please install PyTorch to use TART reranker.")
23
+ self.num_labels = config.num_labels
24
+ self.config = config
25
+
26
+ self.shared = nn.Embedding(config.vocab_size, config.d_model)
27
+
28
+ encoder_config = copy.deepcopy(config)
29
+ encoder_config.use_cache = False
30
+ encoder_config.is_encoder_decoder = False
31
+ self.encoder = T5Stack(encoder_config, self.shared)
32
+
33
+ self.dropout = nn.Dropout(dropout)
34
+ self.classifier = nn.Linear(config.hidden_size, config.num_labels)
35
+
36
+ # Initialize weights and apply final processing
37
+ self.post_init()
38
+
39
+ # Model parallel
40
+ self.model_parallel = False
41
+ self.device_map = None
42
+
43
+ def parallelize(self, device_map=None):
44
+ try:
45
+ import torch
46
+ except ImportError:
47
+ raise ImportError("Please install PyTorch to use TART reranker.")
48
+ self.device_map = (
49
+ get_device_map(len(self.encoder.block), range(torch.cuda.device_count()))
50
+ if device_map is None
51
+ else device_map
52
+ )
53
+ assert_device_map(self.device_map, len(self.encoder.block))
54
+ self.encoder.parallelize(self.device_map)
55
+ self.classifier = self.classifier.to(self.encoder.first_device)
56
+ self.model_parallel = True
57
+
58
+ def deparallelize(self):
59
+ self.encoder.deparallelize()
60
+ self.encoder = self.encoder.to("cpu")
61
+ self.model_parallel = False
62
+ self.device_map = None
63
+ empty_cuda_cache()
64
+
65
+ def get_input_embeddings(self):
66
+ return self.shared
67
+
68
+ def set_input_embeddings(self, new_embeddings):
69
+ self.shared = new_embeddings
70
+ self.encoder.set_input_embeddings(new_embeddings)
71
+
72
+ def get_encoder(self):
73
+ return self.encoder
74
+
75
+ def _prune_heads(self, heads_to_prune):
76
+ """
77
+ Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer} See base
78
+ class PreTrainedModel
79
+ """
80
+ for layer, heads in heads_to_prune.items():
81
+ self.encoder.layer[layer].attention.prune_heads(heads)
82
+
83
+ def forward(
84
+ self,
85
+ input_ids=None,
86
+ attention_mask=None,
87
+ head_mask=None,
88
+ inputs_embeds=None,
89
+ labels=None,
90
+ output_attentions=None,
91
+ output_hidden_states=None,
92
+ return_dict=None,
93
+ ):
94
+ try:
95
+ import torch
96
+ from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
97
+ except ImportError:
98
+ raise ImportError("Please install PyTorch to use TART reranker.")
99
+ return_dict = (
100
+ return_dict if return_dict is not None else self.config.use_return_dict
101
+ )
102
+
103
+ outputs = self.encoder(
104
+ input_ids=input_ids,
105
+ attention_mask=attention_mask,
106
+ inputs_embeds=inputs_embeds,
107
+ head_mask=head_mask,
108
+ output_attentions=output_attentions,
109
+ output_hidden_states=output_hidden_states,
110
+ return_dict=return_dict,
111
+ )
112
+
113
+ hidden_states = outputs[0]
114
+ pooled_output = hidden_states[:, 0, :] # Take bos token (equiv. to <s>)
115
+
116
+ pooled_output = self.dropout(pooled_output)
117
+ logits = self.classifier(pooled_output)
118
+
119
+ loss = None
120
+ if labels is not None:
121
+ if self.config.problem_type is None:
122
+ if self.num_labels == 1:
123
+ self.config.problem_type = "regression"
124
+ elif self.num_labels > 1 and (
125
+ labels.dtype == torch.long or labels.dtype == torch.int
126
+ ):
127
+ self.config.problem_type = "single_label_classification"
128
+ else:
129
+ self.config.problem_type = "multi_label_classification"
130
+
131
+ if self.config.problem_type == "regression":
132
+ loss_fct = MSELoss()
133
+ if self.num_labels == 1:
134
+ loss = loss_fct(logits.squeeze(), labels.squeeze())
135
+ else:
136
+ loss = loss_fct(logits, labels)
137
+ elif self.config.problem_type == "single_label_classification":
138
+ loss_fct = CrossEntropyLoss()
139
+ loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
140
+ elif self.config.problem_type == "multi_label_classification":
141
+ loss_fct = BCEWithLogitsLoss()
142
+ loss = loss_fct(logits, labels)
143
+ if not return_dict:
144
+ output = (logits,) + outputs[1:]
145
+ return ((loss,) + output) if loss is not None else output
146
+
147
+ return SequenceClassifierOutput(
148
+ loss=loss,
149
+ logits=logits,
150
+ hidden_states=outputs.hidden_states,
151
+ attentions=outputs.attentions,
152
+ )
@@ -0,0 +1,139 @@
1
+ from itertools import chain
2
+ from typing import List, Tuple
3
+
4
+ import pandas as pd
5
+
6
+ from autorag.nodes.passagereranker.base import BasePassageReranker
7
+ from autorag.nodes.passagereranker.tart.modeling_enc_t5 import (
8
+ EncT5ForSequenceClassification,
9
+ )
10
+ from autorag.nodes.passagereranker.tart.tokenization_enc_t5 import EncT5Tokenizer
11
+ from autorag.utils.util import (
12
+ make_batch,
13
+ sort_by_scores,
14
+ flatten_apply,
15
+ select_top_k,
16
+ result_to_dataframe,
17
+ empty_cuda_cache,
18
+ )
19
+
20
+
21
+ class Tart(BasePassageReranker):
22
+ def __init__(self, project_dir: str, *args, **kwargs):
23
+ super().__init__(project_dir)
24
+ try:
25
+ import torch
26
+ except ImportError:
27
+ raise ImportError(
28
+ "torch is not installed. Please install torch first to use TART reranker."
29
+ )
30
+ model_name = "facebook/tart-full-flan-t5-xl"
31
+ self.model = EncT5ForSequenceClassification.from_pretrained(model_name)
32
+ self.tokenizer = EncT5Tokenizer.from_pretrained(model_name)
33
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
34
+ self.model = self.model.to(self.device)
35
+
36
+ def __del__(self):
37
+ del self.model
38
+ del self.tokenizer
39
+ empty_cuda_cache()
40
+ super().__del__()
41
+
42
+ @result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
43
+ def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
44
+ queries, contents, _, ids = self.cast_to_run(previous_result)
45
+ top_k = kwargs.pop("top_k")
46
+ instruction = kwargs.pop("instruction", "Find passage to answer given question")
47
+ batch = kwargs.pop("batch", 64)
48
+ return self._pure(queries, contents, ids, top_k, instruction, batch)
49
+
50
+ def _pure(
51
+ self,
52
+ queries: List[str],
53
+ contents_list: List[List[str]],
54
+ ids_list: List[List[str]],
55
+ top_k: int,
56
+ instruction: str = "Find passage to answer given question",
57
+ batch: int = 64,
58
+ ) -> Tuple[List[List[str]], List[List[str]], List[List[float]]]:
59
+ """
60
+ Rerank a list of contents based on their relevance to a query using Tart.
61
+ TART is a reranker based on TART (https://github.com/facebookresearch/tart).
62
+ You can rerank the passages with the instruction using TARTReranker.
63
+ The default model is facebook/tart-full-flan-t5-xl.
64
+
65
+ :param queries: The list of queries to use for reranking
66
+ :param contents_list: The list of lists of contents to rerank
67
+ :param ids_list: The list of lists of ids retrieved from the initial ranking
68
+ :param top_k: The number of passages to be retrieved
69
+ :param instruction: The instruction for reranking.
70
+ Note: default instruction is "Find passage to answer given question"
71
+ The default instruction from the TART paper is being used.
72
+ If you want to use a different instruction, you can change the instruction through this parameter
73
+ :param batch: The number of queries to be processed in a batch
74
+ :return: tuple of lists containing the reranked contents, ids, and scores
75
+ """
76
+ nested_list = [
77
+ [["{} [SEP] {}".format(instruction, query)] for _ in contents]
78
+ for query, contents in zip(queries, contents_list)
79
+ ]
80
+
81
+ rerank_scores = flatten_apply(
82
+ tart_run_model,
83
+ nested_list,
84
+ model=self.model,
85
+ batch_size=batch,
86
+ tokenizer=self.tokenizer,
87
+ device=self.device,
88
+ contents_list=contents_list,
89
+ )
90
+
91
+ df = pd.DataFrame(
92
+ {
93
+ "contents": contents_list,
94
+ "ids": ids_list,
95
+ "scores": rerank_scores,
96
+ }
97
+ )
98
+ df[["contents", "ids", "scores"]] = df.apply(
99
+ sort_by_scores, axis=1, result_type="expand"
100
+ )
101
+ results = select_top_k(df, ["contents", "ids", "scores"], top_k)
102
+
103
+ return (
104
+ results["contents"].tolist(),
105
+ results["ids"].tolist(),
106
+ results["scores"].tolist(),
107
+ )
108
+
109
+
110
+ def tart_run_model(
111
+ input_texts, contents_list, model, batch_size: int, tokenizer, device
112
+ ):
113
+ try:
114
+ import torch
115
+ import torch.nn.functional as F
116
+ except ImportError:
117
+ raise ImportError(
118
+ "torch is not installed. Please install torch first to use TART reranker."
119
+ )
120
+ flattened_texts = list(chain.from_iterable(input_texts))
121
+ flattened_contents = list(chain.from_iterable(contents_list))
122
+ batch_input_texts = make_batch(flattened_texts, batch_size)
123
+ batch_contents_list = make_batch(flattened_contents, batch_size)
124
+ results = []
125
+ for batch_texts, batch_contents in zip(batch_input_texts, batch_contents_list):
126
+ feature = tokenizer(
127
+ batch_texts,
128
+ batch_contents,
129
+ padding=True,
130
+ truncation=True,
131
+ return_tensors="pt",
132
+ ).to(device)
133
+ with torch.no_grad():
134
+ pred_scores = model(**feature).logits
135
+ normalized_scores = [
136
+ float(score[1]) for score in F.softmax(pred_scores, dim=1)
137
+ ]
138
+ results.extend(normalized_scores)
139
+ return results
@@ -0,0 +1,112 @@
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ from transformers import T5Tokenizer
6
+
7
+
8
+ class EncT5Tokenizer(T5Tokenizer):
9
+ def __init__(
10
+ self,
11
+ vocab_file,
12
+ bos_token="<s>",
13
+ eos_token="</s>",
14
+ unk_token="<unk>",
15
+ pad_token="<pad>",
16
+ extra_ids=100,
17
+ additional_special_tokens=None,
18
+ sp_model_kwargs: Optional[Dict[str, Any]] = None,
19
+ **kwargs,
20
+ ) -> None:
21
+ sp_model_kwargs = {} if sp_model_kwargs is None else sp_model_kwargs
22
+
23
+ super().__init__(
24
+ vocab_file=vocab_file,
25
+ bos_token=bos_token,
26
+ eos_token=eos_token,
27
+ unk_token=unk_token,
28
+ pad_token=pad_token,
29
+ extra_ids=extra_ids,
30
+ additional_special_tokens=additional_special_tokens,
31
+ sp_model_kwargs=sp_model_kwargs,
32
+ **kwargs,
33
+ )
34
+
35
+ def get_special_tokens_mask(
36
+ self,
37
+ token_ids_0: List[int],
38
+ token_ids_1: Optional[List[int]] = None,
39
+ already_has_special_tokens: bool = False,
40
+ ) -> List[int]:
41
+ """
42
+ Retrieve sequence ids from a token list that has no special tokens added. This method is called when adding
43
+ special tokens using the tokenizer `prepare_for_model` method.
44
+ Args:
45
+ token_ids_0 (`List[int]`):
46
+ List of IDs.
47
+ token_ids_1 (`List[int]`, *optional*):
48
+ Optional second list of IDs for sequence pairs.
49
+ already_has_special_tokens (`bool`, *optional*, defaults to `False`):
50
+ Whether or not the token list is already formatted with special tokens for the model.
51
+ Returns:
52
+ `List[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token.
53
+ """
54
+ if already_has_special_tokens:
55
+ return super().get_special_tokens_mask(
56
+ token_ids_0=token_ids_0,
57
+ token_ids_1=token_ids_1,
58
+ already_has_special_tokens=True,
59
+ )
60
+
61
+ # normal case: some special tokens
62
+ if token_ids_1 is None:
63
+ return [1] + ([0] * len(token_ids_0)) + [1]
64
+ return [1] + ([0] * len(token_ids_0)) + [1] + ([0] * len(token_ids_1)) + [1]
65
+
66
+ def create_token_type_ids_from_sequences(
67
+ self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None
68
+ ) -> List[int]:
69
+ """
70
+ Create a mask from the two sequences passed to be used in a sequence-pair classification task. T5 does not make
71
+ use of token type ids, therefore a list of zeros is returned.
72
+ Args:
73
+ token_ids_0 (`List[int]`):
74
+ List of IDs.
75
+ token_ids_1 (`List[int]`, *optional*):
76
+ Optional second list of IDs for sequence pairs.
77
+ Returns:
78
+ `List[int]`: List of zeros.
79
+ """
80
+ bos = [self.bos_token_id]
81
+ eos = [self.eos_token_id]
82
+
83
+ if token_ids_1 is None:
84
+ return len(bos + token_ids_0 + eos) * [0]
85
+ return len(bos + token_ids_0 + eos + token_ids_1 + eos) * [0]
86
+
87
+ def build_inputs_with_special_tokens(
88
+ self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None
89
+ ) -> List[int]:
90
+ """
91
+ Build model inputs from a sequence or a pair of sequence for sequence classification tasks by concatenating and
92
+ adding special tokens. A sequence has the following format:
93
+ - single sequence: `<s> X </s>`
94
+ - pair of sequences: `<s> A </s> B </s>`
95
+ Args:
96
+ token_ids_0 (`List[int]`):
97
+ List of IDs to which the special tokens will be added.
98
+ token_ids_1 (`List[int]`, *optional*):
99
+ Optional second list of IDs for sequence pairs.
100
+ Returns:
101
+ `List[int]`: List of [input IDs](../glossary#input-ids) with the appropriate special tokens.
102
+ """
103
+ if token_ids_1 is None:
104
+ return [self.bos_token_id] + token_ids_0 + [self.eos_token_id]
105
+ else:
106
+ return (
107
+ [self.bos_token_id]
108
+ + token_ids_0
109
+ + [self.eos_token_id]
110
+ + token_ids_1
111
+ + [self.eos_token_id]
112
+ )
@@ -0,0 +1,72 @@
1
+ import os
2
+ from datetime import datetime
3
+ from typing import List, Tuple
4
+
5
+ import pandas as pd
6
+
7
+ from autorag.nodes.passagereranker.base import BasePassageReranker
8
+ from autorag.utils import result_to_dataframe, fetch_contents
9
+
10
+
11
+ class TimeReranker(BasePassageReranker):
12
+ def __init__(self, project_dir: str, *args, **kwargs):
13
+ super().__init__(project_dir, *args, **kwargs)
14
+ self.corpus_df = pd.read_parquet(
15
+ os.path.join(project_dir, "data", "corpus.parquet"), engine="pyarrow"
16
+ )
17
+
18
+ @result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
19
+ def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
20
+ _, contents, scores, ids = self.cast_to_run(previous_result)
21
+ metadatas = fetch_contents(self.corpus_df, ids, column_name="metadata")
22
+ times = [
23
+ [time["last_modified_datetime"] for time in time_list]
24
+ for time_list in metadatas
25
+ ]
26
+ top_k = kwargs.pop("top_k")
27
+ return self._pure(contents, scores, ids, top_k, times)
28
+
29
+ def _pure(
30
+ self,
31
+ contents_list: List[List[str]],
32
+ scores_list: List[List[float]],
33
+ ids_list: List[List[str]],
34
+ top_k: int,
35
+ time_list: List[List[datetime]],
36
+ ) -> Tuple[List[List[str]], List[List[str]], List[List[float]]]:
37
+ """
38
+ Rerank the passages based on merely the datetime of the passage.
39
+ It uses 'last_modified_datetime' key in the corpus metadata,
40
+ so the metadata should be in the format of {'last_modified_datetime': datetime.datetime} at the corpus data file.
41
+
42
+ :param contents_list: The list of lists of contents
43
+ :param scores_list: The list of lists of scores from the initial ranking
44
+ :param ids_list: The list of lists of ids
45
+ :param top_k: The number of passages to be retrieved after reranking
46
+ :param time_list: The metadata list of lists of datetime.datetime
47
+ It automatically extracts the 'last_modified_datetime' key from the metadata in the corpus data.
48
+ :return: The reranked contents, ids, and scores
49
+ """
50
+
51
+ def sort_row(contents, scores, ids, time, top_k):
52
+ combined = list(zip(contents, scores, ids, time))
53
+ combined.sort(key=lambda x: x[3], reverse=True)
54
+ sorted_contents, sorted_scores, sorted_ids, _ = zip(*combined)
55
+ return (
56
+ list(sorted_contents)[:top_k],
57
+ list(sorted_scores)[:top_k],
58
+ list(sorted_ids)[:top_k],
59
+ )
60
+
61
+ reranked_contents, reranked_scores, reranked_ids = zip(
62
+ *map(
63
+ sort_row,
64
+ contents_list,
65
+ scores_list,
66
+ ids_list,
67
+ time_list,
68
+ [top_k] * len(contents_list),
69
+ )
70
+ )
71
+
72
+ return list(reranked_contents), list(reranked_ids), list(reranked_scores)
@@ -0,0 +1,160 @@
1
+ import logging
2
+ from typing import List, Tuple
3
+
4
+ import pandas as pd
5
+
6
+ from autorag.nodes.passagereranker.base import BasePassageReranker
7
+ from autorag.utils import result_to_dataframe
8
+ from autorag.utils.util import select_top_k, sort_by_scores, empty_cuda_cache
9
+
10
+ logger = logging.getLogger("AutoRAG")
11
+
12
+
13
+ class Upr(BasePassageReranker):
14
+ def __init__(
15
+ self,
16
+ project_dir: str,
17
+ use_bf16: bool = False,
18
+ prefix_prompt: str = "Passage: ",
19
+ suffix_prompt: str = "Please write a question based on this passage.",
20
+ *args,
21
+ **kwargs,
22
+ ):
23
+ """
24
+ Initialize the UPR reranker node.
25
+
26
+ :param project_dir: The project directory
27
+ :param use_bf16: Whether to use bfloat16 for the model. Default is False.
28
+ :param prefix_prompt: The prefix prompt for the language model that generates question for reranking.
29
+ Default is "Passage: ".
30
+ The prefix prompt serves as the initial context or instruction for the language model.
31
+ It sets the stage for what is expected in the output
32
+ :param suffix_prompt: The suffix prompt for the language model that generates question for reranking.
33
+ Default is "Please write a question based on this passage.".
34
+ The suffix prompt provides a cue or a closing instruction to the language model,
35
+ signaling how to conclude the generated text or what format to follow at the end.
36
+ :param kwargs: Extra arguments
37
+ """
38
+ super().__init__(project_dir, *args, **kwargs)
39
+
40
+ self.scorer = UPRScorer(
41
+ suffix_prompt=suffix_prompt, prefix_prompt=prefix_prompt, use_bf16=use_bf16
42
+ )
43
+
44
+ def __del__(self):
45
+ del self.scorer
46
+ super().__del__()
47
+
48
+ @result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
49
+ def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
50
+ queries, contents, _, ids = self.cast_to_run(previous_result)
51
+ top_k = kwargs.pop("top_k")
52
+ return self._pure(queries, contents, ids, top_k)
53
+
54
+ def _pure(
55
+ self,
56
+ queries: List[str],
57
+ contents_list: List[List[str]],
58
+ ids_list: List[List[str]],
59
+ top_k: int,
60
+ ) -> Tuple[List[List[str]], List[List[str]], List[List[float]]]:
61
+ """
62
+ Rerank a list of contents based on their relevance to a query using UPR.
63
+ UPR is a reranker based on UPR (https://github.com/DevSinghSachan/unsupervised-passage-reranking).
64
+ The language model will make a question based on the passage and rerank the passages by the likelihood of the question.
65
+ The default model is t5-large.
66
+
67
+ :param queries: The list of queries to use for reranking
68
+ :param contents_list: The list of lists of contents to rerank
69
+ :param ids_list: The list of lists of ids retrieved from the initial ranking
70
+ :param top_k: The number of passages to be retrieved
71
+
72
+ :return: tuple of lists containing the reranked contents, ids, and scores
73
+ """
74
+ df = pd.DataFrame(
75
+ {
76
+ "query": queries,
77
+ "contents": contents_list,
78
+ "ids": ids_list,
79
+ }
80
+ )
81
+
82
+ df["scores"] = df.apply(
83
+ lambda row: self.scorer.compute(
84
+ query=row["query"], contents=row["contents"]
85
+ ),
86
+ axis=1,
87
+ )
88
+ df[["contents", "ids", "scores"]] = df.apply(
89
+ lambda x: sort_by_scores(x, reverse=False), axis=1, result_type="expand"
90
+ )
91
+ results = select_top_k(df, ["contents", "ids", "scores"], top_k)
92
+ return (
93
+ results["contents"].tolist(),
94
+ results["ids"].tolist(),
95
+ results["scores"].tolist(),
96
+ )
97
+
98
+
99
+ class UPRScorer:
100
+ def __init__(self, suffix_prompt: str, prefix_prompt: str, use_bf16: bool = False):
101
+ try:
102
+ import torch
103
+ from transformers import T5Tokenizer, T5ForConditionalGeneration
104
+ except ImportError:
105
+ raise ImportError(
106
+ "torch is not installed. Please install torch to use UPRReranker."
107
+ )
108
+ model_name = "t5-large"
109
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
110
+ self.tokenizer = T5Tokenizer.from_pretrained(model_name)
111
+ self.model = T5ForConditionalGeneration.from_pretrained(
112
+ model_name, torch_dtype=torch.bfloat16 if use_bf16 else torch.float32
113
+ ).to(self.device)
114
+ self.suffix_prompt = suffix_prompt
115
+ self.prefix_prompt = prefix_prompt
116
+
117
+ def compute(self, query: str, contents: List[str]) -> List[float]:
118
+ try:
119
+ import torch
120
+ except ImportError:
121
+ raise ImportError(
122
+ "torch is not installed. Please install torch to use UPRReranker."
123
+ )
124
+ query_token = self.tokenizer(
125
+ query, max_length=128, truncation=True, return_tensors="pt"
126
+ )
127
+ prompts = list(
128
+ map(
129
+ lambda content: f"{self.prefix_prompt} {content} {self.suffix_prompt}",
130
+ contents,
131
+ )
132
+ )
133
+ prompt_token_outputs = self.tokenizer(
134
+ prompts,
135
+ padding="longest",
136
+ max_length=512,
137
+ pad_to_multiple_of=8,
138
+ truncation=True,
139
+ return_tensors="pt",
140
+ )
141
+
142
+ query_input_ids = torch.repeat_interleave(
143
+ query_token["input_ids"], len(contents), dim=0
144
+ ).to(self.device)
145
+
146
+ with torch.no_grad():
147
+ logits = self.model(
148
+ input_ids=prompt_token_outputs["input_ids"].to(self.device),
149
+ attention_mask=prompt_token_outputs["attention_mask"].to(self.device),
150
+ labels=query_input_ids,
151
+ ).logits
152
+ log_softmax = torch.nn.functional.log_softmax(logits, dim=-1)
153
+ nll = -log_softmax.gather(2, query_input_ids.unsqueeze(2)).squeeze(2)
154
+ avg_nll = torch.sum(nll, dim=1)
155
+ return avg_nll.tolist()
156
+
157
+ def __del__(self):
158
+ del self.model
159
+ del self.tokenizer
160
+ empty_cuda_cache()