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,155 @@
|
|
|
1
|
+
from typing import List, Union
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
from autorag.embedding.base import EmbeddingModel
|
|
7
|
+
from autorag.evaluation.metric.util import calculate_cosine_similarity
|
|
8
|
+
from autorag.nodes.passageaugmenter.base import BasePassageAugmenter
|
|
9
|
+
from autorag.utils.util import (
|
|
10
|
+
filter_dict_keys,
|
|
11
|
+
fetch_contents,
|
|
12
|
+
embedding_query_content,
|
|
13
|
+
result_to_dataframe,
|
|
14
|
+
empty_cuda_cache,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PrevNextPassageAugmenter(BasePassageAugmenter):
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
project_dir: str,
|
|
22
|
+
embedding_model: Union[str, dict] = "openai",
|
|
23
|
+
*args,
|
|
24
|
+
**kwargs,
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Initialize the PrevNextPassageAugmenter module.
|
|
28
|
+
|
|
29
|
+
:param project_dir:
|
|
30
|
+
:param embedding_model: The embedding model name to use for calculating cosine similarity
|
|
31
|
+
Default is openai (text-embedding-ada-002)
|
|
32
|
+
:param kwargs:
|
|
33
|
+
"""
|
|
34
|
+
super().__init__(project_dir, *args, **kwargs)
|
|
35
|
+
slim_corpus_df = self.corpus_df[["doc_id", "metadata"]]
|
|
36
|
+
slim_corpus_df.loc[:, "metadata"] = slim_corpus_df["metadata"].apply(
|
|
37
|
+
filter_dict_keys, keys=["prev_id", "next_id"]
|
|
38
|
+
)
|
|
39
|
+
self.slim_corpus_df = slim_corpus_df
|
|
40
|
+
|
|
41
|
+
# init embedding model
|
|
42
|
+
self.embedding_model = EmbeddingModel.load(embedding_model)()
|
|
43
|
+
|
|
44
|
+
def __del__(self):
|
|
45
|
+
del self.embedding_model
|
|
46
|
+
empty_cuda_cache()
|
|
47
|
+
super().__del__()
|
|
48
|
+
|
|
49
|
+
@result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
|
|
50
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
51
|
+
"""
|
|
52
|
+
Run the passage augmenter node - PrevNextPassageAugmenter module.
|
|
53
|
+
|
|
54
|
+
:param previous_result: The previous result Dataframe.
|
|
55
|
+
:param top_k: You must input the top_k value to get the top k results.
|
|
56
|
+
:param kwargs: Not affected.
|
|
57
|
+
:return: DataFrame with retrieved_contents, retrieved_ids, and retrieve_scores columns
|
|
58
|
+
"""
|
|
59
|
+
top_k = kwargs.pop("top_k")
|
|
60
|
+
|
|
61
|
+
ids = self.cast_to_run(previous_result)
|
|
62
|
+
# find queries columns
|
|
63
|
+
assert "query" in previous_result.columns, (
|
|
64
|
+
"previous_result must have query column."
|
|
65
|
+
)
|
|
66
|
+
queries = previous_result["query"].tolist()
|
|
67
|
+
|
|
68
|
+
mode = kwargs.pop("mode", "both")
|
|
69
|
+
num_passages = kwargs.pop("num_passages", 1)
|
|
70
|
+
augmented_ids = self._pure(ids, num_passages, mode)
|
|
71
|
+
|
|
72
|
+
# fetch contents from corpus to use augmented ids
|
|
73
|
+
augmented_contents = fetch_contents(self.corpus_df, augmented_ids)
|
|
74
|
+
|
|
75
|
+
query_embeddings, contents_embeddings = embedding_query_content(
|
|
76
|
+
queries, augmented_contents, self.embedding_model, batch=128
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# get scores from calculated cosine similarity
|
|
80
|
+
augmented_scores = [
|
|
81
|
+
np.array(
|
|
82
|
+
[
|
|
83
|
+
calculate_cosine_similarity(query_embedding, x)
|
|
84
|
+
for x in content_embeddings
|
|
85
|
+
]
|
|
86
|
+
).tolist()
|
|
87
|
+
for query_embedding, content_embeddings in zip(
|
|
88
|
+
query_embeddings, contents_embeddings
|
|
89
|
+
)
|
|
90
|
+
]
|
|
91
|
+
return self.sort_by_scores(
|
|
92
|
+
augmented_contents, augmented_ids, augmented_scores, top_k
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def _pure(
|
|
96
|
+
self,
|
|
97
|
+
ids_list: List[List[str]],
|
|
98
|
+
num_passages: int = 1,
|
|
99
|
+
mode: str = "both",
|
|
100
|
+
) -> List[List[str]]:
|
|
101
|
+
"""
|
|
102
|
+
Add passages before and/or after the retrieved passage.
|
|
103
|
+
For more information, visit https://docs.llamaindex.ai/en/stable/examples/node_postprocessor/PrevNextPostprocessorDemo/.
|
|
104
|
+
|
|
105
|
+
:param ids_list: The list of lists of ids retrieved
|
|
106
|
+
:param num_passages: The number of passages to add before and after the retrieved passage
|
|
107
|
+
Default is 1.
|
|
108
|
+
:param mode: The mode of augmentation
|
|
109
|
+
'prev': add passages before the retrieved passage
|
|
110
|
+
'next': add passages after the retrieved passage
|
|
111
|
+
'both': add passages before and after the retrieved passage
|
|
112
|
+
Default is 'next'.
|
|
113
|
+
:return: The list of lists of augmented ids
|
|
114
|
+
"""
|
|
115
|
+
if mode not in ["prev", "next", "both"]:
|
|
116
|
+
raise ValueError(f"mode must be 'prev', 'next', or 'both', but got {mode}")
|
|
117
|
+
|
|
118
|
+
augmented_ids = [
|
|
119
|
+
(
|
|
120
|
+
lambda ids: prev_next_augmenter_pure(
|
|
121
|
+
ids, self.slim_corpus_df, mode, num_passages
|
|
122
|
+
)
|
|
123
|
+
)(ids)
|
|
124
|
+
for ids in ids_list
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
return augmented_ids
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def prev_next_augmenter_pure(
|
|
131
|
+
ids: List[str], corpus_df: pd.DataFrame, mode: str, num_passages: int
|
|
132
|
+
):
|
|
133
|
+
def fetch_id_sequence(start_id, key):
|
|
134
|
+
sequence = []
|
|
135
|
+
current_id = start_id
|
|
136
|
+
for _ in range(num_passages):
|
|
137
|
+
current_id = (
|
|
138
|
+
corpus_df.loc[corpus_df["doc_id"] == current_id]["metadata"]
|
|
139
|
+
.values[0]
|
|
140
|
+
.get(key)
|
|
141
|
+
)
|
|
142
|
+
if current_id is None:
|
|
143
|
+
break
|
|
144
|
+
sequence.append(current_id)
|
|
145
|
+
return sequence
|
|
146
|
+
|
|
147
|
+
augmented_group = []
|
|
148
|
+
for id_ in ids:
|
|
149
|
+
current_ids = [id_]
|
|
150
|
+
if mode in ["prev", "both"]:
|
|
151
|
+
current_ids = fetch_id_sequence(id_, "prev_id")[::-1] + current_ids
|
|
152
|
+
if mode in ["next", "both"]:
|
|
153
|
+
current_ids += fetch_id_sequence(id_, "next_id")
|
|
154
|
+
augmented_group.extend(current_ids)
|
|
155
|
+
return augmented_group
|
|
@@ -0,0 +1,131 @@
|
|
|
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_augmenter_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
|
+
if not os.path.exists(node_line_dir):
|
|
24
|
+
os.makedirs(node_line_dir)
|
|
25
|
+
project_dir = pathlib.PurePath(node_line_dir).parent.parent
|
|
26
|
+
qa_df = pd.read_parquet(
|
|
27
|
+
os.path.join(project_dir, "data", "qa.parquet"), engine="pyarrow"
|
|
28
|
+
)
|
|
29
|
+
retrieval_gt = qa_df["retrieval_gt"].tolist()
|
|
30
|
+
retrieval_gt = apply_recursive(lambda x: str(x), to_list(retrieval_gt))
|
|
31
|
+
|
|
32
|
+
results, execution_times = zip(
|
|
33
|
+
*map(
|
|
34
|
+
lambda task: measure_speed(
|
|
35
|
+
task[0].run_evaluator,
|
|
36
|
+
project_dir=project_dir,
|
|
37
|
+
previous_result=previous_result,
|
|
38
|
+
**task[1],
|
|
39
|
+
),
|
|
40
|
+
zip(modules, module_params),
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
average_times = list(map(lambda x: x / len(results[0]), execution_times))
|
|
44
|
+
metric_inputs = [
|
|
45
|
+
MetricInput(retrieval_gt=ret_gt, query=query, generation_gt=gen_gt)
|
|
46
|
+
for ret_gt, query, gen_gt in zip(
|
|
47
|
+
retrieval_gt,
|
|
48
|
+
previous_result["query"].tolist(),
|
|
49
|
+
previous_result["generation_gt"].tolist(),
|
|
50
|
+
)
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
# run metrics before filtering
|
|
54
|
+
if strategies.get("metrics") is None:
|
|
55
|
+
raise ValueError(
|
|
56
|
+
"You must at least one metrics for passage_augmenter evaluation."
|
|
57
|
+
)
|
|
58
|
+
results = list(
|
|
59
|
+
map(
|
|
60
|
+
lambda x: evaluate_retrieval_node(
|
|
61
|
+
x,
|
|
62
|
+
metric_inputs,
|
|
63
|
+
strategies.get("metrics"),
|
|
64
|
+
),
|
|
65
|
+
results,
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# save results to folder
|
|
70
|
+
save_dir = os.path.join(node_line_dir, "passage_augmenter") # node name
|
|
71
|
+
if not os.path.exists(save_dir):
|
|
72
|
+
os.makedirs(save_dir)
|
|
73
|
+
filepaths = list(
|
|
74
|
+
map(lambda x: os.path.join(save_dir, f"{x}.parquet"), range(len(modules)))
|
|
75
|
+
)
|
|
76
|
+
list(
|
|
77
|
+
map(lambda x: x[0].to_parquet(x[1], index=False), zip(results, filepaths))
|
|
78
|
+
) # execute save to parquet
|
|
79
|
+
filenames = list(map(lambda x: os.path.basename(x), filepaths))
|
|
80
|
+
|
|
81
|
+
summary_df = pd.DataFrame(
|
|
82
|
+
{
|
|
83
|
+
"filename": filenames,
|
|
84
|
+
"module_name": list(map(lambda module: module.__name__, modules)),
|
|
85
|
+
"module_params": module_params,
|
|
86
|
+
"execution_time": average_times,
|
|
87
|
+
**{
|
|
88
|
+
f"passage_augmenter_{metric}": list(
|
|
89
|
+
map(lambda result: result[metric].mean(), results)
|
|
90
|
+
)
|
|
91
|
+
for metric in strategies.get("metrics")
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# filter by strategies
|
|
97
|
+
if strategies.get("speed_threshold") is not None:
|
|
98
|
+
results, filenames = filter_by_threshold(
|
|
99
|
+
results, average_times, strategies["speed_threshold"], filenames
|
|
100
|
+
)
|
|
101
|
+
selected_result, selected_filename = select_best(
|
|
102
|
+
results,
|
|
103
|
+
strategies.get("metrics"),
|
|
104
|
+
filenames,
|
|
105
|
+
strategies.get("strategy", "mean"),
|
|
106
|
+
)
|
|
107
|
+
# change metric name columns to passage_augmenter_metric_name
|
|
108
|
+
selected_result = selected_result.rename(
|
|
109
|
+
columns={
|
|
110
|
+
metric_name: f"passage_augmenter_{metric_name}"
|
|
111
|
+
for metric_name in strategies["metrics"]
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
# drop retrieval result columns in previous_result
|
|
115
|
+
previous_result = previous_result.drop(
|
|
116
|
+
columns=["retrieved_contents", "retrieved_ids", "retrieve_scores"]
|
|
117
|
+
)
|
|
118
|
+
best_result = pd.concat([previous_result, selected_result], axis=1)
|
|
119
|
+
|
|
120
|
+
# add 'is_best' column to summary file
|
|
121
|
+
summary_df["is_best"] = summary_df["filename"] == selected_filename
|
|
122
|
+
|
|
123
|
+
# save files
|
|
124
|
+
summary_df.to_csv(os.path.join(save_dir, "summary.csv"), index=False)
|
|
125
|
+
best_result.to_parquet(
|
|
126
|
+
os.path.join(
|
|
127
|
+
save_dir, f"best_{os.path.splitext(selected_filename)[0]}.parquet"
|
|
128
|
+
),
|
|
129
|
+
index=False,
|
|
130
|
+
)
|
|
131
|
+
return best_result
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from llama_index.core.llms import LLM
|
|
7
|
+
|
|
8
|
+
from autorag import generator_models
|
|
9
|
+
from autorag.schema import BaseModule
|
|
10
|
+
from autorag.utils import result_to_dataframe
|
|
11
|
+
from autorag.utils.cast import cast_retrieved_contents
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger("AutoRAG")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BasePassageCompressor(BaseModule, metaclass=abc.ABCMeta):
|
|
17
|
+
def __init__(self, project_dir: str, *args, **kwargs):
|
|
18
|
+
logger.info(
|
|
19
|
+
f"Initialize passage compressor node - {self.__class__.__name__} module..."
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def __del__(self):
|
|
23
|
+
logger.info(
|
|
24
|
+
f"Deleting passage compressor node - {self.__class__.__name__} module..."
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def cast_to_run(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
28
|
+
logger.info(
|
|
29
|
+
f"Running passage compressor node - {self.__class__.__name__} module..."
|
|
30
|
+
)
|
|
31
|
+
assert "query" in previous_result.columns, (
|
|
32
|
+
"previous_result must contain 'query' column."
|
|
33
|
+
)
|
|
34
|
+
assert len(previous_result) > 0, "previous_result must have at least one row."
|
|
35
|
+
|
|
36
|
+
queries = previous_result["query"].tolist()
|
|
37
|
+
|
|
38
|
+
return queries, cast_retrieved_contents(previous_result)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class LlamaIndexCompressor(BasePassageCompressor, metaclass=abc.ABCMeta):
|
|
42
|
+
param_list = ["prompt", "chat_prompt", "batch"]
|
|
43
|
+
|
|
44
|
+
def __init__(self, project_dir: str, **kwargs):
|
|
45
|
+
"""
|
|
46
|
+
Initialize passage compressor module.
|
|
47
|
+
|
|
48
|
+
:param project_dir: The project directory
|
|
49
|
+
:param llm: The llm name that will be used to summarize.
|
|
50
|
+
The LlamaIndex LLM model can be used in here.
|
|
51
|
+
:param kwargs: Extra parameter for init llm
|
|
52
|
+
"""
|
|
53
|
+
super().__init__(project_dir)
|
|
54
|
+
kwargs_dict = dict(
|
|
55
|
+
filter(lambda x: x[0] not in self.param_list, kwargs.items())
|
|
56
|
+
)
|
|
57
|
+
llm_name = kwargs_dict.pop("llm")
|
|
58
|
+
self.llm: LLM = make_llm(llm_name, kwargs_dict)
|
|
59
|
+
|
|
60
|
+
def __del__(self):
|
|
61
|
+
del self.llm
|
|
62
|
+
super().__del__()
|
|
63
|
+
|
|
64
|
+
@result_to_dataframe(["retrieved_contents"])
|
|
65
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
66
|
+
queries, retrieved_contents = self.cast_to_run(previous_result)
|
|
67
|
+
param_dict = dict(filter(lambda x: x[0] in self.param_list, kwargs.items()))
|
|
68
|
+
result = self._pure(queries, retrieved_contents, **param_dict)
|
|
69
|
+
return list(map(lambda x: [x], result))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def make_llm(llm_name: str, kwargs: Dict) -> LLM:
|
|
73
|
+
if llm_name not in generator_models:
|
|
74
|
+
raise KeyError(
|
|
75
|
+
f"{llm_name} is not supported. "
|
|
76
|
+
"You can add it manually by calling autorag.generator_models."
|
|
77
|
+
)
|
|
78
|
+
return generator_models[llm_name](**kwargs)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from autorag.nodes.passagecompressor.base import BasePassageCompressor
|
|
6
|
+
from autorag.utils.util import pop_params, result_to_dataframe, empty_cuda_cache
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# TODO: Parallel Processing Refactoring at #460
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LongLLMLingua(BasePassageCompressor):
|
|
13
|
+
def __init__(
|
|
14
|
+
self, project_dir: str, model_name: str = "NousResearch/Llama-2-7b-hf", **kwargs
|
|
15
|
+
):
|
|
16
|
+
try:
|
|
17
|
+
from llmlingua import PromptCompressor
|
|
18
|
+
except ImportError:
|
|
19
|
+
raise ImportError(
|
|
20
|
+
"LongLLMLingua is not installed. Please install it by running `pip install llmlingua`."
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
super().__init__(project_dir)
|
|
24
|
+
model_init_params = pop_params(PromptCompressor.__init__, kwargs)
|
|
25
|
+
self.llm_lingua = PromptCompressor(model_name=model_name, **model_init_params)
|
|
26
|
+
|
|
27
|
+
def __del__(self):
|
|
28
|
+
del self.llm_lingua
|
|
29
|
+
empty_cuda_cache()
|
|
30
|
+
super().__del__()
|
|
31
|
+
|
|
32
|
+
@result_to_dataframe(["retrieved_contents"])
|
|
33
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
34
|
+
queries, retrieved_contents = self.cast_to_run(previous_result)
|
|
35
|
+
results = self._pure(queries, retrieved_contents, **kwargs)
|
|
36
|
+
return list(map(lambda x: [x], results))
|
|
37
|
+
|
|
38
|
+
def _pure(
|
|
39
|
+
self,
|
|
40
|
+
queries: List[str],
|
|
41
|
+
contents: List[List[str]],
|
|
42
|
+
instructions: Optional[str] = None,
|
|
43
|
+
target_token: int = 300,
|
|
44
|
+
**kwargs,
|
|
45
|
+
) -> List[str]:
|
|
46
|
+
"""
|
|
47
|
+
Compresses the retrieved texts using LongLLMLingua.
|
|
48
|
+
For more information, visit https://github.com/microsoft/LLMLingua.
|
|
49
|
+
|
|
50
|
+
:param queries: The queries for retrieved passages.
|
|
51
|
+
:param contents: The contents of retrieved passages.
|
|
52
|
+
:param model_name: The model name to use for compression.
|
|
53
|
+
The default is "NousResearch/Llama-2-7b-hf".
|
|
54
|
+
:param instructions: The instructions for compression.
|
|
55
|
+
Default is None. When it is None, it will use default instructions.
|
|
56
|
+
:param target_token: The target token for compression.
|
|
57
|
+
Default is 300.
|
|
58
|
+
:param kwargs: Additional keyword arguments.
|
|
59
|
+
:return: The list of compressed texts.
|
|
60
|
+
"""
|
|
61
|
+
if instructions is None:
|
|
62
|
+
instructions = "Given the context, please answer the final question"
|
|
63
|
+
results = [
|
|
64
|
+
llmlingua_pure(
|
|
65
|
+
query, contents_, self.llm_lingua, instructions, target_token, **kwargs
|
|
66
|
+
)
|
|
67
|
+
for query, contents_ in zip(queries, contents)
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
return results
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def llmlingua_pure(
|
|
74
|
+
query: str,
|
|
75
|
+
contents: List[str],
|
|
76
|
+
llm_lingua,
|
|
77
|
+
instructions: str,
|
|
78
|
+
target_token: int = 300,
|
|
79
|
+
**kwargs,
|
|
80
|
+
) -> str:
|
|
81
|
+
"""
|
|
82
|
+
Return the compressed text.
|
|
83
|
+
|
|
84
|
+
:param query: The query for retrieved passages.
|
|
85
|
+
:param contents: The contents of retrieved passages.
|
|
86
|
+
:param llm_lingua: The llm instance, that will be used to compress.
|
|
87
|
+
:param instructions: The instructions for compression.
|
|
88
|
+
:param target_token: The target token for compression.
|
|
89
|
+
Default is 300.
|
|
90
|
+
:param kwargs: Additional keyword arguments.
|
|
91
|
+
:return: The compressed text.
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
from llmlingua import PromptCompressor
|
|
95
|
+
except ImportError:
|
|
96
|
+
raise ImportError(
|
|
97
|
+
"LongLLMLingua is not installed. Please install it by running `pip install llmlingua`."
|
|
98
|
+
)
|
|
99
|
+
# split by "\n\n" (recommended by LongLLMLingua authors)
|
|
100
|
+
new_context_texts = [c for context in contents for c in context.split("\n\n")]
|
|
101
|
+
compress_prompt_params = pop_params(PromptCompressor.compress_prompt, kwargs)
|
|
102
|
+
compressed_prompt = llm_lingua.compress_prompt(
|
|
103
|
+
new_context_texts,
|
|
104
|
+
question=query,
|
|
105
|
+
instruction=instructions,
|
|
106
|
+
rank_method="longllmlingua",
|
|
107
|
+
target_token=target_token,
|
|
108
|
+
**compress_prompt_params,
|
|
109
|
+
)
|
|
110
|
+
compressed_prompt_txt = compressed_prompt["compressed_prompt"]
|
|
111
|
+
|
|
112
|
+
# separate out the question and instruction
|
|
113
|
+
result = "\n\n".join(compressed_prompt_txt.split("\n\n")[1:-1])
|
|
114
|
+
|
|
115
|
+
return result
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from autorag.nodes.passagecompressor.base import BasePassageCompressor
|
|
6
|
+
from autorag.utils import result_to_dataframe
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PassCompressor(BasePassageCompressor):
|
|
10
|
+
@result_to_dataframe(["retrieved_contents"])
|
|
11
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
12
|
+
_, contents = self.cast_to_run(previous_result)
|
|
13
|
+
return self._pure(contents)
|
|
14
|
+
|
|
15
|
+
def _pure(self, contents: List[List[str]]):
|
|
16
|
+
return contents
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from llama_index.core import PromptTemplate
|
|
4
|
+
from llama_index.core.prompts import PromptType
|
|
5
|
+
from llama_index.core.prompts.utils import is_chat_model
|
|
6
|
+
from llama_index.core.response_synthesizers import Refine as rf
|
|
7
|
+
|
|
8
|
+
from autorag.nodes.passagecompressor.base import LlamaIndexCompressor
|
|
9
|
+
from autorag.utils.util import get_event_loop, process_batch
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Refine(LlamaIndexCompressor):
|
|
13
|
+
def _pure(
|
|
14
|
+
self,
|
|
15
|
+
queries: List[str],
|
|
16
|
+
contents: List[List[str]],
|
|
17
|
+
prompt: Optional[str] = None,
|
|
18
|
+
chat_prompt: Optional[str] = None,
|
|
19
|
+
batch: int = 16,
|
|
20
|
+
) -> List[str]:
|
|
21
|
+
"""
|
|
22
|
+
Refine a response to a query across text chunks.
|
|
23
|
+
This function is a wrapper for llama_index.response_synthesizers.Refine.
|
|
24
|
+
For more information, visit https://docs.llamaindex.ai/en/stable/examples/response_synthesizers/refine/.
|
|
25
|
+
|
|
26
|
+
:param queries: The queries for retrieved passages.
|
|
27
|
+
:param contents: The contents of retrieved passages.
|
|
28
|
+
:param prompt: The prompt template for refine.
|
|
29
|
+
If you want to use chat prompt, you should pass chat_prompt instead.
|
|
30
|
+
At prompt, you must specify where to put 'context_msg' and 'query_str'.
|
|
31
|
+
Default is None. When it is None, it will use llama index default prompt.
|
|
32
|
+
:param chat_prompt: The chat prompt template for refine.
|
|
33
|
+
If you want to use normal prompt, you should pass prompt instead.
|
|
34
|
+
At prompt, you must specify where to put 'context_msg' and 'query_str'.
|
|
35
|
+
Default is None. When it is None, it will use llama index default chat prompt.
|
|
36
|
+
:param batch: The batch size for llm.
|
|
37
|
+
Set low if you face some errors.
|
|
38
|
+
Default is 16.
|
|
39
|
+
:return: The list of compressed texts.
|
|
40
|
+
"""
|
|
41
|
+
if prompt is not None and not is_chat_model(self.llm):
|
|
42
|
+
refine_template = PromptTemplate(prompt, prompt_type=PromptType.REFINE)
|
|
43
|
+
elif chat_prompt is not None and is_chat_model(self.llm):
|
|
44
|
+
refine_template = PromptTemplate(chat_prompt, prompt_type=PromptType.REFINE)
|
|
45
|
+
else:
|
|
46
|
+
refine_template = None
|
|
47
|
+
summarizer = rf(llm=self.llm, refine_template=refine_template, verbose=True)
|
|
48
|
+
tasks = [
|
|
49
|
+
summarizer.aget_response(query, content)
|
|
50
|
+
for query, content in zip(queries, contents)
|
|
51
|
+
]
|
|
52
|
+
loop = get_event_loop()
|
|
53
|
+
results = loop.run_until_complete(process_batch(tasks, batch_size=batch))
|
|
54
|
+
return results
|