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,322 @@
1
+ import logging
2
+ from typing import Callable, Optional, Dict, Awaitable, Any, Tuple, List
3
+ import uuid
4
+ import pandas as pd
5
+ from autorag.utils.util import process_batch, get_event_loop, fetch_contents
6
+
7
+ from autorag.support import get_support_modules
8
+
9
+ logger = logging.getLogger("AutoRAG")
10
+
11
+
12
+ class Raw:
13
+ """
14
+ The Raw class that stored document parsing results.
15
+ It can do chunking.
16
+ It has two column names, 'raw_id' and 'contents'.
17
+ """
18
+
19
+ def __init__(self, raw_df: Optional[pd.DataFrame] = None):
20
+ self.data = raw_df
21
+
22
+ def batch_apply(
23
+ self, fn: Callable[[Dict, Any], Awaitable[Dict]], batch_size: int = 32, **kwargs
24
+ ) -> "Raw":
25
+ raw_dicts = self.data.to_dict(orient="records")
26
+ loop = get_event_loop()
27
+ tasks = [fn(raw_dict, **kwargs) for raw_dict in raw_dicts]
28
+ results = loop.run_until_complete(process_batch(tasks, batch_size))
29
+ return Raw(pd.DataFrame(results))
30
+
31
+ def map(self, fn: Callable[[pd.DataFrame, Any], pd.DataFrame], **kwargs) -> "Raw":
32
+ return Raw(fn(self.data, **kwargs))
33
+
34
+ def flatmap(self, fn: Callable, **kwargs) -> "Raw":
35
+ return fn(self.data, **kwargs)
36
+
37
+ def chunk(self, module_name: str, **module_params) -> "Corpus":
38
+ chunk_module = get_support_modules(module_name)
39
+ chunked_result = chunk_module(parsed_result=self.data, **module_params)
40
+ return Corpus(chunked_result, self)
41
+
42
+ def __add__(self, other):
43
+ assert isinstance(other, Raw), "You can only add Raw instances."
44
+ self.data = pd.concat([self.data, other.data], ignore_index=True).reset_index(
45
+ drop=True
46
+ )
47
+ return self
48
+
49
+
50
+ class Corpus:
51
+ """
52
+ The Corpus class that stored chunked passages.
53
+ It can generate qa set, linked with Raw instance.
54
+ """
55
+
56
+ def __init__(
57
+ self,
58
+ corpus_df: Optional[pd.DataFrame] = None,
59
+ linked_raw: Optional[Raw] = None,
60
+ ):
61
+ self.data = corpus_df
62
+ self._linked_raw = linked_raw
63
+
64
+ @property
65
+ def linked_raw(self) -> Raw:
66
+ return self._linked_raw
67
+
68
+ @linked_raw.setter
69
+ def linked_raw(self, raw: Raw):
70
+ raise NotImplementedError("linked_raw is read-only.")
71
+
72
+ def to_parquet(self, save_path: str):
73
+ """
74
+ Save the corpus to the AutoRAG compatible parquet file.
75
+ It is not for the data creation, for running AutoRAG.
76
+ If you want to save it directly, use the below code.
77
+ `corpus.data.to_parquet(save_path)`
78
+
79
+ :param save_path: The path to save the corpus.
80
+ """
81
+ if not save_path.endswith(".parquet"):
82
+ raise ValueError("save_path must be ended with .parquet")
83
+ save_df = self.data.reset_index(drop=True)
84
+ save_df.to_parquet(save_path)
85
+
86
+ def batch_apply(
87
+ self, fn: Callable[[Dict, Any], Awaitable[Dict]], batch_size: int = 32, **kwargs
88
+ ) -> "Corpus":
89
+ corpus_dicts = self.data.to_dict(orient="records")
90
+ loop = get_event_loop()
91
+ tasks = [fn(corpus_dict, **kwargs) for corpus_dict in corpus_dicts]
92
+ results = loop.run_until_complete(process_batch(tasks, batch_size))
93
+ return Corpus(pd.DataFrame(results), self.linked_raw)
94
+
95
+ def map(
96
+ self, fn: Callable[[pd.DataFrame, Any], pd.DataFrame], **kwargs
97
+ ) -> "Corpus":
98
+ return Corpus(fn(self.data, **kwargs), self.linked_raw)
99
+
100
+ def sample(self, fn: Callable[[pd.DataFrame, Any], pd.DataFrame], **kwargs) -> "QA":
101
+ """
102
+ Sample the corpus for making QA.
103
+ It selects the subset of the corpus and makes QA set from it.
104
+ You can generate questions from the created question.
105
+ It is the first step to make QA set from the corpus.
106
+ If you select just one passage from each passage, it will be a single-hop QA set.
107
+ If you select multiple passages from each passage, it will be a multi-hop QA set.
108
+
109
+ :param fn: The select function to perform.
110
+ It returns QA dataframe.
111
+ :return: QA instance that is selected.
112
+ It contains qid and retrieval_gt columns.
113
+ """
114
+ return QA(fn(self.data, **kwargs), self)
115
+
116
+
117
+ class QA:
118
+ def __init__(
119
+ self,
120
+ qa_df: Optional[pd.DataFrame] = None,
121
+ linked_corpus: Optional[Corpus] = None,
122
+ ):
123
+ self.data = qa_df
124
+ self._linked_corpus = linked_corpus
125
+
126
+ @property
127
+ def linked_corpus(self) -> Corpus:
128
+ return self._linked_corpus
129
+
130
+ @linked_corpus.setter
131
+ def linked_corpus(self, corpus: Corpus):
132
+ raise NotImplementedError("linked_corpus is read-only.")
133
+
134
+ def batch_apply(
135
+ self, fn: Callable[[Dict, Any], Awaitable[Dict]], batch_size: int = 32, **kwargs
136
+ ) -> "QA":
137
+ qa_dicts = self.data.to_dict(orient="records")
138
+ loop = get_event_loop()
139
+ tasks = [fn(qa_dict, **kwargs) for qa_dict in qa_dicts]
140
+ results = loop.run_until_complete(process_batch(tasks, batch_size))
141
+
142
+ # Experimental feature
143
+ if fn.__name__ == "multiple_queries_gen":
144
+ return self._process_multiple_queries_gen(results)
145
+
146
+ return QA(pd.DataFrame(results), self.linked_corpus)
147
+
148
+ def batch_filter(
149
+ self, fn: Callable[[Dict, Any], Awaitable[bool]], batch_size: int = 32, **kwargs
150
+ ) -> "QA":
151
+ qa_dicts = self.data.to_dict(orient="records")
152
+ loop = get_event_loop()
153
+ tasks = [fn(qa_dict, **kwargs) for qa_dict in qa_dicts]
154
+ masks = loop.run_until_complete(process_batch(tasks, batch_size))
155
+ return QA(self.data[masks], self.linked_corpus)
156
+
157
+ def filter(self, fn: Callable[[Dict, Any], bool], **kwargs) -> "QA":
158
+ qa_dicts = self.data.to_dict(orient="records")
159
+ masks = [fn(qa_dict, **kwargs) for qa_dict in qa_dicts]
160
+ return QA(self.data[masks], self.linked_corpus)
161
+
162
+ def map(self, fn: Callable[[pd.DataFrame, Any], pd.DataFrame], **kwargs) -> "QA":
163
+ return QA(fn(self.data, **kwargs), self.linked_corpus)
164
+
165
+ def make_retrieval_gt_contents(self) -> "QA":
166
+ """
167
+ Make retrieval_gt_contents column from retrieval_gt column.
168
+ :return: The QA instance that has a retrieval_gt_contents column.
169
+ """
170
+ self.data["retrieval_gt_contents"] = self.data["retrieval_gt"].apply(
171
+ lambda x: fetch_contents(self.linked_corpus.data, x)
172
+ )
173
+ return self
174
+
175
+ def to_parquet(self, qa_save_path: str, corpus_save_path: str):
176
+ """
177
+ Save the qa and corpus to the AutoRAG compatible parquet file.
178
+ It is not for the data creation, for running AutoRAG.
179
+ If you want to save it directly, use the below code.
180
+ `qa.data.to_parquet(save_path)`
181
+
182
+ :param qa_save_path: The path to save the qa dataset.
183
+ :param corpus_save_path: The path to save the corpus.
184
+ """
185
+ if not qa_save_path.endswith(".parquet"):
186
+ raise ValueError("save_path must be ended with .parquet")
187
+ if not corpus_save_path.endswith(".parquet"):
188
+ raise ValueError("save_path must be ended with .parquet")
189
+ save_df = self.data[
190
+ ["qid", "query", "retrieval_gt", "generation_gt"]
191
+ ].reset_index(drop=True)
192
+ save_df.to_parquet(qa_save_path)
193
+ self.linked_corpus.to_parquet(corpus_save_path)
194
+
195
+ def update_corpus(self, new_corpus: Corpus) -> "QA":
196
+ """
197
+ Update linked corpus.
198
+ Not just replace linked_corpus to the new Corpus,
199
+ it replaces the whole `retrieval_gt` to the new corpus using `linked_raw`.
200
+ The QA data must have a `retrieval_gt` column.
201
+
202
+ :param new_corpus: Corpus that you want to replace.
203
+ Must have valid `linked_raw` and `raw_id`, `raw_start_idx`, `raw_end_idx` columns.
204
+ :return: The QA instance that updated linked corpus.
205
+ """
206
+ self.data["evidence_path"] = (
207
+ self.data["retrieval_gt"]
208
+ .apply(
209
+ lambda x: fetch_contents(
210
+ self.linked_corpus.data,
211
+ x,
212
+ column_name="path",
213
+ )
214
+ )
215
+ .tolist()
216
+ )
217
+ self.data["evidence_page"] = self.data["retrieval_gt"].apply(
218
+ lambda x: list(
219
+ map(
220
+ lambda lst: list(map(lambda x: x.get("page", -1), lst)),
221
+ fetch_contents(self.linked_corpus.data, x, column_name="metadata"),
222
+ )
223
+ )
224
+ )
225
+ if "evidence_start_end_idx" not in self.data.columns:
226
+ # make evidence start_end_idx
227
+ self.data["evidence_start_end_idx"] = (
228
+ self.data["retrieval_gt"]
229
+ .apply(
230
+ lambda x: fetch_contents(
231
+ self.linked_corpus.data,
232
+ x,
233
+ column_name="start_end_idx",
234
+ )
235
+ )
236
+ .tolist()
237
+ )
238
+
239
+ # matching the new corpus with the old corpus
240
+ path_corpus_dict = QA.__make_path_corpus_dict(new_corpus.data)
241
+ new_retrieval_gt = self.data.apply(
242
+ lambda row: QA.__match_index_row(
243
+ row["evidence_start_end_idx"],
244
+ row["evidence_path"],
245
+ row["evidence_page"],
246
+ path_corpus_dict,
247
+ ),
248
+ axis=1,
249
+ ).tolist()
250
+ new_qa = self.data.copy(deep=True)[["qid", "query", "generation_gt"]]
251
+ new_qa["retrieval_gt"] = new_retrieval_gt
252
+ return QA(new_qa, new_corpus)
253
+
254
+ @staticmethod
255
+ def __match_index(target_idx: Tuple[int, int], dst_idx: Tuple[int, int]) -> bool:
256
+ """
257
+ Check if the target_idx is overlap by the dst_idx.
258
+ """
259
+ target_start, target_end = target_idx
260
+ dst_start, dst_end = dst_idx
261
+ return (
262
+ dst_start <= target_start <= dst_end or dst_start <= target_end <= dst_end
263
+ )
264
+
265
+ @staticmethod
266
+ def __match_index_row(
267
+ evidence_indices: List[List[Tuple[int, int]]],
268
+ evidence_paths: List[List[str]],
269
+ evidence_pages: List[List[int]],
270
+ path_corpus_dict: Dict,
271
+ ) -> List[List[str]]:
272
+ """
273
+ Find the matched passage from new_corpus.
274
+
275
+ :param evidence_indices: The evidence indices at the corresponding Raw.
276
+ Its shape is the same as the retrieval_gt.
277
+ :param evidence_paths: The evidence paths at the corresponding Raw.
278
+ Its shape is the same as the retrieval_gt.
279
+ :param path_corpus_dict: The key is the path name, and the value is the corpus dataframe that only contains the path in the key.
280
+ You can make it using `QA.__make_path_corpus_dict`.
281
+ :return:
282
+ """
283
+ result = []
284
+ for i, idx_list in enumerate(evidence_indices):
285
+ sub_result = []
286
+ for j, idx in enumerate(idx_list):
287
+ path_corpus_df = path_corpus_dict[evidence_paths[i][j]]
288
+ if evidence_pages[i][j] >= 0:
289
+ path_corpus_df = path_corpus_df.loc[
290
+ path_corpus_df["metadata"].apply(lambda x: x.get("page", -1))
291
+ == evidence_pages[i][j]
292
+ ]
293
+ matched_corpus = path_corpus_df.loc[
294
+ path_corpus_df["start_end_idx"].apply(
295
+ lambda x: QA.__match_index(idx, x)
296
+ )
297
+ ]
298
+ sub_result.extend(matched_corpus["doc_id"].tolist())
299
+ result.append(sub_result)
300
+ return result
301
+
302
+ @staticmethod
303
+ def __make_path_corpus_dict(corpus_df: pd.DataFrame) -> Dict[str, pd.DataFrame]:
304
+ return {
305
+ path: corpus_df[corpus_df["path"] == path]
306
+ for path in corpus_df["path"].unique()
307
+ }
308
+
309
+ # Experimental feature
310
+ def _process_multiple_queries_gen(self, results: List[Dict]) -> "QA":
311
+ data = []
312
+ for result in results:
313
+ queries = result["query"].split("\n")
314
+ for query in queries:
315
+ new_result = {
316
+ key: (str(uuid.uuid4()) if key == "qid" else result[key])
317
+ for key in result.keys()
318
+ }
319
+ new_result["query"] = query
320
+ data.append(new_result)
321
+ df = pd.DataFrame(data)
322
+ return QA(df, self.linked_corpus)
File without changes
@@ -0,0 +1,103 @@
1
+ import mimetypes
2
+ import os
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+ from typing import Dict, List, Tuple, Callable
6
+
7
+ import pandas as pd
8
+ import yaml
9
+ from langchain_core.documents import Document
10
+ from llama_index.core.schema import NodeRelationship
11
+
12
+ from autorag.schema import Module
13
+ from autorag.utils.util import make_combinations, explode
14
+
15
+
16
+ def get_file_metadata(file_path: str) -> Dict:
17
+ """Get some handy metadate from filesystem.
18
+
19
+ Args:
20
+ file_path: str: file path in str
21
+ """
22
+ return {
23
+ "file_path": file_path,
24
+ "file_name": os.path.basename(file_path),
25
+ "file_type": mimetypes.guess_type(file_path)[0],
26
+ "file_size": os.path.getsize(file_path),
27
+ "creation_datetime": datetime.fromtimestamp(
28
+ Path(file_path).stat().st_ctime
29
+ ).strftime("%Y-%m-%d"),
30
+ "last_modified_datetime": datetime.fromtimestamp(
31
+ Path(file_path).stat().st_mtime
32
+ ).strftime("%Y-%m-%d"),
33
+ "last_accessed_datetime": datetime.fromtimestamp(
34
+ Path(file_path).stat().st_atime
35
+ ).strftime("%Y-%m-%d"),
36
+ }
37
+
38
+
39
+ def add_essential_metadata(metadata: Dict) -> Dict:
40
+ if "last_modified_datetime" not in metadata:
41
+ metadata["last_modified_datetime"] = datetime.now()
42
+ return metadata
43
+
44
+
45
+ def corpus_df_to_langchain_documents(corpus_df: pd.DataFrame) -> List[Document]:
46
+ page_contents = corpus_df["contents"].tolist()
47
+ ids = corpus_df["doc_id"].tolist()
48
+ metadatas = corpus_df["metadata"].tolist()
49
+ return list(
50
+ map(
51
+ lambda x: Document(page_content=x[0], metadata={"filename": x[1], **x[2]}),
52
+ zip(page_contents, ids, metadatas),
53
+ )
54
+ )
55
+
56
+
57
+ def add_essential_metadata_llama_text_node(metadata: Dict, relationships: Dict) -> Dict:
58
+ if "last_modified_datetime" not in metadata:
59
+ metadata["last_modified_datetime"] = datetime.now()
60
+
61
+ if "prev_id" not in metadata:
62
+ if NodeRelationship.PREVIOUS in relationships:
63
+ prev_node = relationships.get(NodeRelationship.PREVIOUS, None)
64
+ if prev_node:
65
+ metadata["prev_id"] = prev_node.node_id
66
+
67
+ if "next_id" not in metadata:
68
+ if NodeRelationship.NEXT in relationships:
69
+ next_node = relationships.get(NodeRelationship.NEXT, None)
70
+ if next_node:
71
+ metadata["next_id"] = next_node.node_id
72
+ return metadata
73
+
74
+
75
+ def load_yaml(yaml_path: str):
76
+ if not os.path.exists(yaml_path):
77
+ raise ValueError(f"YAML file {yaml_path} does not exist.")
78
+ with open(yaml_path, "r", encoding="utf-8") as stream:
79
+ try:
80
+ yaml_dict = yaml.safe_load(stream)
81
+ except yaml.YAMLError as exc:
82
+ raise ValueError(f"YAML file {yaml_path} could not be loaded.") from exc
83
+ return yaml_dict["modules"]
84
+
85
+
86
+ def get_param_combinations(modules: List[Dict]) -> Tuple[List[Callable], List[Dict]]:
87
+ module_callable_list, module_params_list = [], []
88
+ for module in modules:
89
+ module_instance = Module.from_dict(module)
90
+ module_params_list.append(module_instance.module_param)
91
+ module_callable_list.append(module_instance.module)
92
+
93
+ combinations = list(map(make_combinations, module_params_list))
94
+ module_list, combination_list = explode(module_callable_list, combinations)
95
+ return module_list, combination_list
96
+
97
+
98
+ def get_start_end_idx(original_text: str, search_str: str) -> Tuple[int, int]:
99
+ start_idx = original_text.find(search_str)
100
+ if start_idx == -1:
101
+ return 0, 0
102
+ end_idx = start_idx + len(search_str)
103
+ return start_idx, end_idx - 1
@@ -0,0 +1,9 @@
1
+ from .base import (
2
+ extract_node_line_names,
3
+ extract_node_strategy,
4
+ summary_df_to_yaml,
5
+ extract_best_config,
6
+ Runner,
7
+ )
8
+ from .api import ApiRunner
9
+ from .gradio import GradioRunner