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,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
|