flashrag-dev 0.1.1__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.
- flashrag/__init__.py +0 -0
- flashrag/config/__init__.py +2 -0
- flashrag/config/config.py +219 -0
- flashrag/dataset/__init__.py +2 -0
- flashrag/dataset/dataset.py +160 -0
- flashrag/dataset/utils.py +60 -0
- flashrag/evaluator/__init__.py +2 -0
- flashrag/evaluator/_bleu.py +209 -0
- flashrag/evaluator/evaluator.py +82 -0
- flashrag/evaluator/metrics.py +508 -0
- flashrag/evaluator/utils.py +19 -0
- flashrag/generator/__init__.py +2 -0
- flashrag/generator/fid.py +247 -0
- flashrag/generator/generator.py +623 -0
- flashrag/generator/openai_generator.py +96 -0
- flashrag/generator/stop_word_criteria.py +105 -0
- flashrag/judger/__init__.py +1 -0
- flashrag/judger/judger.py +184 -0
- flashrag/pipeline/__init__.py +3 -0
- flashrag/pipeline/active_pipeline.py +980 -0
- flashrag/pipeline/branching_pipeline.py +250 -0
- flashrag/pipeline/pipeline.py +248 -0
- flashrag/pipeline/replug_utils.py +249 -0
- flashrag/prompt/__init__.py +1 -0
- flashrag/prompt/base_prompt.py +165 -0
- flashrag/prompt/selfask_examplars.py +108 -0
- flashrag/prompt/trace_examplars.py +4015 -0
- flashrag/refiner/__init__.py +2 -0
- flashrag/refiner/kg_refiner.py +612 -0
- flashrag/refiner/llmlingua_compressor.py +2360 -0
- flashrag/refiner/refiner.py +252 -0
- flashrag/refiner/selective_context_compressor.py +294 -0
- flashrag/retriever/__init__.py +3 -0
- flashrag/retriever/__main__.py +4 -0
- flashrag/retriever/encoder.py +120 -0
- flashrag/retriever/index_builder.py +334 -0
- flashrag/retriever/reranker.py +145 -0
- flashrag/retriever/retriever.py +346 -0
- flashrag/retriever/utils.py +49 -0
- flashrag/utils/__init__.py +2 -0
- flashrag/utils/constants.py +51 -0
- flashrag/utils/pred_parse.py +25 -0
- flashrag/utils/utils.py +135 -0
- flashrag_dev-0.1.1.dist-info/LICENSE +21 -0
- flashrag_dev-0.1.1.dist-info/METADATA +616 -0
- flashrag_dev-0.1.1.dist-info/RECORD +48 -0
- flashrag_dev-0.1.1.dist-info/WHEEL +5 -0
- flashrag_dev-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import List, Dict
|
|
7
|
+
import functools
|
|
8
|
+
from tqdm import tqdm
|
|
9
|
+
import faiss
|
|
10
|
+
|
|
11
|
+
from flashrag.utils import get_reranker
|
|
12
|
+
from flashrag.retriever.utils import load_corpus, load_docs
|
|
13
|
+
from flashrag.retriever.encoder import Encoder, STEncoder
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def cache_manager(func):
|
|
17
|
+
"""
|
|
18
|
+
Decorator used for retrieving document cache.
|
|
19
|
+
With the decorator, The retriever can store each retrieved document as a file and reuse it.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@functools.wraps(func)
|
|
23
|
+
def wrapper(self, query_list, num=None, return_score=False):
|
|
24
|
+
if num is None:
|
|
25
|
+
num = self.topk
|
|
26
|
+
if self.use_cache:
|
|
27
|
+
if isinstance(query_list, str):
|
|
28
|
+
new_query_list = [query_list]
|
|
29
|
+
else:
|
|
30
|
+
new_query_list = query_list
|
|
31
|
+
|
|
32
|
+
no_cache_query = []
|
|
33
|
+
cache_results = []
|
|
34
|
+
for query in new_query_list:
|
|
35
|
+
if query in self.cache:
|
|
36
|
+
cache_res = self.cache[query]
|
|
37
|
+
if len(cache_res) < num:
|
|
38
|
+
warnings.warn(f"The number of cached retrieval results is less than topk ({num})")
|
|
39
|
+
cache_res = cache_res[:num]
|
|
40
|
+
# separate the doc score
|
|
41
|
+
doc_scores = [item.pop("score") for item in cache_res]
|
|
42
|
+
cache_results.append((cache_res, doc_scores))
|
|
43
|
+
else:
|
|
44
|
+
cache_results.append(None)
|
|
45
|
+
no_cache_query.append(query)
|
|
46
|
+
|
|
47
|
+
if no_cache_query != []:
|
|
48
|
+
# use batch search without decorator
|
|
49
|
+
no_cache_results, no_cache_scores = self._batch_search_with_rerank(no_cache_query, num, True)
|
|
50
|
+
no_cache_idx = 0
|
|
51
|
+
for idx, res in enumerate(cache_results):
|
|
52
|
+
if res is None:
|
|
53
|
+
assert new_query_list[idx] == no_cache_query[no_cache_idx]
|
|
54
|
+
cache_results = (
|
|
55
|
+
no_cache_results[no_cache_idx],
|
|
56
|
+
no_cache_scores[no_cache_scores],
|
|
57
|
+
)
|
|
58
|
+
no_cache_idx += 1
|
|
59
|
+
|
|
60
|
+
results, scores = (
|
|
61
|
+
[t[0] for t in cache_results],
|
|
62
|
+
[t[1] for t in cache_results],
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
else:
|
|
66
|
+
results, scores = func(self, query_list, num, True)
|
|
67
|
+
|
|
68
|
+
if self.save_cache:
|
|
69
|
+
# merge result and score
|
|
70
|
+
save_results = results.copy()
|
|
71
|
+
save_scores = scores.copy()
|
|
72
|
+
if isinstance(query_list, str):
|
|
73
|
+
query_list = [query_list]
|
|
74
|
+
if "batch" not in func.__name__:
|
|
75
|
+
save_results = [save_results]
|
|
76
|
+
save_scores = [save_scores]
|
|
77
|
+
for query, doc_items, doc_scores in zip(query_list, save_results, save_scores):
|
|
78
|
+
for item, score in zip(doc_items, doc_scores):
|
|
79
|
+
item["score"] = score
|
|
80
|
+
self.cache[query] = doc_items
|
|
81
|
+
|
|
82
|
+
if return_score:
|
|
83
|
+
return results, scores
|
|
84
|
+
else:
|
|
85
|
+
return results
|
|
86
|
+
|
|
87
|
+
return wrapper
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def rerank_manager(func):
|
|
91
|
+
"""
|
|
92
|
+
Decorator used for reranking retrieved documents.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
@functools.wraps(func)
|
|
96
|
+
def wrapper(self, query_list, num=None, return_score=False):
|
|
97
|
+
results, scores = func(self, query_list, num, True)
|
|
98
|
+
if self.use_reranker:
|
|
99
|
+
results, scores = self.reranker.rerank(query_list, results)
|
|
100
|
+
if "batch" not in func.__name__:
|
|
101
|
+
results = results[0]
|
|
102
|
+
scores = scores[0]
|
|
103
|
+
if return_score:
|
|
104
|
+
return results, scores
|
|
105
|
+
else:
|
|
106
|
+
return results
|
|
107
|
+
|
|
108
|
+
return wrapper
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class BaseRetriever:
|
|
112
|
+
"""Base object for all retrievers."""
|
|
113
|
+
|
|
114
|
+
def __init__(self, config):
|
|
115
|
+
self.config = config
|
|
116
|
+
self.retrieval_method = config["retrieval_method"]
|
|
117
|
+
self.topk = config["retrieval_topk"]
|
|
118
|
+
|
|
119
|
+
self.index_path = config["index_path"]
|
|
120
|
+
self.corpus_path = config["corpus_path"]
|
|
121
|
+
|
|
122
|
+
self.save_cache = config["save_retrieval_cache"]
|
|
123
|
+
self.use_cache = config["use_retrieval_cache"]
|
|
124
|
+
self.cache_path = config["retrieval_cache_path"]
|
|
125
|
+
|
|
126
|
+
self.use_reranker = config["use_reranker"]
|
|
127
|
+
if self.use_reranker:
|
|
128
|
+
self.reranker = get_reranker(config)
|
|
129
|
+
|
|
130
|
+
if self.save_cache:
|
|
131
|
+
self.cache_save_path = os.path.join(config["save_dir"], "retrieval_cache.json")
|
|
132
|
+
self.cache = {}
|
|
133
|
+
if self.use_cache:
|
|
134
|
+
assert self.cache_path is not None
|
|
135
|
+
with open(self.cache_path, "r") as f:
|
|
136
|
+
self.cache = json.load(f)
|
|
137
|
+
|
|
138
|
+
def _save_cache(self):
|
|
139
|
+
with open(self.cache_save_path, "w") as f:
|
|
140
|
+
json.dump(self.cache, f, indent=4)
|
|
141
|
+
|
|
142
|
+
def _search(self, query: str, num: int, return_score: bool) -> List[Dict[str, str]]:
|
|
143
|
+
r"""Retrieve topk relevant documents in corpus.
|
|
144
|
+
|
|
145
|
+
Return:
|
|
146
|
+
list: contains information related to the document, including:
|
|
147
|
+
contents: used for building index
|
|
148
|
+
title: (if provided)
|
|
149
|
+
text: (if provided)
|
|
150
|
+
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
def _batch_search(self, query_list, num, return_score):
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
@cache_manager
|
|
159
|
+
@rerank_manager
|
|
160
|
+
def search(self, *args, **kwargs):
|
|
161
|
+
return self._search(*args, **kwargs)
|
|
162
|
+
|
|
163
|
+
@cache_manager
|
|
164
|
+
@rerank_manager
|
|
165
|
+
def batch_search(self, *args, **kwargs):
|
|
166
|
+
return self._batch_search(*args, **kwargs)
|
|
167
|
+
|
|
168
|
+
@rerank_manager
|
|
169
|
+
def _batch_search_with_rerank(self, *args, **kwargs):
|
|
170
|
+
return self._batch_search(*args, **kwargs)
|
|
171
|
+
|
|
172
|
+
@rerank_manager
|
|
173
|
+
def _search_with_rerank(self, *args, **kwargs):
|
|
174
|
+
return self._search(*args, **kwargs)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class BM25Retriever(BaseRetriever):
|
|
178
|
+
r"""BM25 retriever based on pre-built pyserini index."""
|
|
179
|
+
|
|
180
|
+
def __init__(self, config):
|
|
181
|
+
super().__init__(config)
|
|
182
|
+
self.backend = config['bm25_backend']
|
|
183
|
+
|
|
184
|
+
if self.backend == 'pyserini':
|
|
185
|
+
# Warning: the method based on pyserini will be deprecated
|
|
186
|
+
from pyserini.search.lucene import LuceneSearcher
|
|
187
|
+
|
|
188
|
+
self.searcher = LuceneSearcher(self.index_path)
|
|
189
|
+
self.contain_doc = self._check_contain_doc()
|
|
190
|
+
if not self.contain_doc:
|
|
191
|
+
self.corpus = load_corpus(self.corpus_path)
|
|
192
|
+
self.max_process_num = 8
|
|
193
|
+
elif self.backend == 'bm25s':
|
|
194
|
+
import Stemmer
|
|
195
|
+
import bm25s
|
|
196
|
+
|
|
197
|
+
stemmer = Stemmer.Stemmer('english')
|
|
198
|
+
self.searcher = bm25s.BM25.load(self.index_path, mmap=True, load_corpus=True)
|
|
199
|
+
self.searcher.backend = 'numba'
|
|
200
|
+
self.tokenizer = bm25s.tokenization.Tokenizer(stemmer=stemmer)
|
|
201
|
+
else:
|
|
202
|
+
assert False, 'Invalid bm25 backend!'
|
|
203
|
+
|
|
204
|
+
def _check_contain_doc(self):
|
|
205
|
+
r"""Check if the index contains document content"""
|
|
206
|
+
return self.searcher.doc(0).raw() is not None
|
|
207
|
+
|
|
208
|
+
def _search(self, query: str, num: int = None, return_score=False) -> List[Dict[str, str]]:
|
|
209
|
+
if num is None:
|
|
210
|
+
num = self.topk
|
|
211
|
+
if self.backend == 'pyserini':
|
|
212
|
+
hits = self.searcher.search(query, num)
|
|
213
|
+
if len(hits) < 1:
|
|
214
|
+
if return_score:
|
|
215
|
+
return [], []
|
|
216
|
+
else:
|
|
217
|
+
return []
|
|
218
|
+
|
|
219
|
+
scores = [hit.score for hit in hits]
|
|
220
|
+
if len(hits) < num:
|
|
221
|
+
warnings.warn("Not enough documents retrieved!")
|
|
222
|
+
else:
|
|
223
|
+
hits = hits[:num]
|
|
224
|
+
|
|
225
|
+
if self.contain_doc:
|
|
226
|
+
all_contents = [json.loads(self.searcher.doc(hit.docid).raw())["contents"] for hit in hits]
|
|
227
|
+
results = [
|
|
228
|
+
{
|
|
229
|
+
"title": content.split("\n")[0].strip('"'),
|
|
230
|
+
"text": "\n".join(content.split("\n")[1:]),
|
|
231
|
+
"contents": content,
|
|
232
|
+
}
|
|
233
|
+
for content in all_contents
|
|
234
|
+
]
|
|
235
|
+
else:
|
|
236
|
+
results = load_docs(self.corpus, [hit.docid for hit in hits])
|
|
237
|
+
elif self.backend == 'bm25s':
|
|
238
|
+
query_tokens = self.tokenizer.tokenize([query])
|
|
239
|
+
results, scores = self.searcher.retrieve(query_tokens, k=num)
|
|
240
|
+
results = results[0]
|
|
241
|
+
scores = scores[0]
|
|
242
|
+
else:
|
|
243
|
+
assert False, 'Invalid bm25 backend!'
|
|
244
|
+
|
|
245
|
+
if return_score:
|
|
246
|
+
return results, scores
|
|
247
|
+
else:
|
|
248
|
+
return results
|
|
249
|
+
|
|
250
|
+
def _batch_search(self, query_list, num: int = None, return_score=False):
|
|
251
|
+
if self.backend == 'pyserini':
|
|
252
|
+
# TODO: modify batch method
|
|
253
|
+
results = []
|
|
254
|
+
scores = []
|
|
255
|
+
for query in query_list:
|
|
256
|
+
item_result, item_score = self._search(query, num, True)
|
|
257
|
+
results.append(item_result)
|
|
258
|
+
scores.append(item_score)
|
|
259
|
+
elif self.backend == 'bm25s':
|
|
260
|
+
query_tokens = self.tokenizer.tokenize(query_list)
|
|
261
|
+
results, scores = self.searcher.retrieve(query_tokens, k=num)
|
|
262
|
+
else:
|
|
263
|
+
assert False, 'Invalid bm25 backend!'
|
|
264
|
+
|
|
265
|
+
if return_score:
|
|
266
|
+
return results, scores
|
|
267
|
+
else:
|
|
268
|
+
return results
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class DenseRetriever(BaseRetriever):
|
|
272
|
+
r"""Dense retriever based on pre-built faiss index."""
|
|
273
|
+
|
|
274
|
+
def __init__(self, config: dict):
|
|
275
|
+
super().__init__(config)
|
|
276
|
+
self.index = faiss.read_index(self.index_path)
|
|
277
|
+
if config["faiss_gpu"]:
|
|
278
|
+
co = faiss.GpuMultipleClonerOptions()
|
|
279
|
+
co.useFloat16 = True
|
|
280
|
+
co.shard = True
|
|
281
|
+
self.index = faiss.index_cpu_to_all_gpus(self.index, co=co)
|
|
282
|
+
|
|
283
|
+
self.corpus = load_corpus(self.corpus_path)
|
|
284
|
+
|
|
285
|
+
if config["use_sentence_transformer"]:
|
|
286
|
+
self.encoder = STEncoder(
|
|
287
|
+
model_name=self.retrieval_method,
|
|
288
|
+
model_path=config["retrieval_model_path"],
|
|
289
|
+
max_length=config["retrieval_query_max_length"],
|
|
290
|
+
use_fp16=config["retrieval_use_fp16"],
|
|
291
|
+
)
|
|
292
|
+
else:
|
|
293
|
+
self.encoder = Encoder(
|
|
294
|
+
model_name=self.retrieval_method,
|
|
295
|
+
model_path=config["retrieval_model_path"],
|
|
296
|
+
pooling_method=config["retrieval_pooling_method"],
|
|
297
|
+
max_length=config["retrieval_query_max_length"],
|
|
298
|
+
use_fp16=config["retrieval_use_fp16"],
|
|
299
|
+
)
|
|
300
|
+
self.topk = config["retrieval_topk"]
|
|
301
|
+
self.batch_size = self.config["retrieval_batch_size"]
|
|
302
|
+
|
|
303
|
+
def _search(self, query: str, num: int = None, return_score=False):
|
|
304
|
+
if num is None:
|
|
305
|
+
num = self.topk
|
|
306
|
+
query_emb = self.encoder.encode(query)
|
|
307
|
+
scores, idxs = self.index.search(query_emb, k=num)
|
|
308
|
+
scores = scores.tolist()
|
|
309
|
+
idxs = idxs[0]
|
|
310
|
+
scores = scores[0]
|
|
311
|
+
|
|
312
|
+
results = load_docs(self.corpus, idxs)
|
|
313
|
+
if return_score:
|
|
314
|
+
return results, scores
|
|
315
|
+
else:
|
|
316
|
+
return results
|
|
317
|
+
|
|
318
|
+
def _batch_search(self, query_list: List[str], num: int = None, return_score=False):
|
|
319
|
+
if isinstance(query_list, str):
|
|
320
|
+
query_list = [query_list]
|
|
321
|
+
if num is None:
|
|
322
|
+
num = self.topk
|
|
323
|
+
|
|
324
|
+
batch_size = self.batch_size
|
|
325
|
+
|
|
326
|
+
results = []
|
|
327
|
+
scores = []
|
|
328
|
+
|
|
329
|
+
for start_idx in tqdm(range(0, len(query_list), batch_size), desc="Retrieval process: "):
|
|
330
|
+
query_batch = query_list[start_idx : start_idx + batch_size]
|
|
331
|
+
batch_emb = self.encoder.encode(query_batch)
|
|
332
|
+
batch_scores, batch_idxs = self.index.search(batch_emb, k=num)
|
|
333
|
+
batch_scores = batch_scores.tolist()
|
|
334
|
+
batch_idxs = batch_idxs.tolist()
|
|
335
|
+
|
|
336
|
+
flat_idxs = sum(batch_idxs, [])
|
|
337
|
+
batch_results = load_docs(self.corpus, flat_idxs)
|
|
338
|
+
batch_results = [batch_results[i * num : (i + 1) * num] for i in range(len(batch_idxs))]
|
|
339
|
+
|
|
340
|
+
scores.extend(batch_scores)
|
|
341
|
+
results.extend(batch_results)
|
|
342
|
+
|
|
343
|
+
if return_score:
|
|
344
|
+
return results, scores
|
|
345
|
+
else:
|
|
346
|
+
return results
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import datasets
|
|
3
|
+
from transformers import AutoTokenizer, AutoModel, AutoConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def load_model(model_path: str, use_fp16: bool = False):
|
|
7
|
+
model_config = AutoConfig.from_pretrained(model_path, trust_remote_code=True)
|
|
8
|
+
model = AutoModel.from_pretrained(model_path, trust_remote_code=True)
|
|
9
|
+
model.eval()
|
|
10
|
+
model.cuda()
|
|
11
|
+
if use_fp16:
|
|
12
|
+
model = model.half()
|
|
13
|
+
tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=True, trust_remote_code=True)
|
|
14
|
+
|
|
15
|
+
return model, tokenizer
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def pooling(pooler_output, last_hidden_state, attention_mask=None, pooling_method="mean"):
|
|
19
|
+
if pooling_method == "mean":
|
|
20
|
+
last_hidden = last_hidden_state.masked_fill(~attention_mask[..., None].bool(), 0.0)
|
|
21
|
+
return last_hidden.sum(dim=1) / attention_mask.sum(dim=1)[..., None]
|
|
22
|
+
elif pooling_method == "cls":
|
|
23
|
+
return last_hidden_state[:, 0]
|
|
24
|
+
elif pooling_method == "pooler":
|
|
25
|
+
return pooler_output
|
|
26
|
+
else:
|
|
27
|
+
raise NotImplementedError("Pooling method not implemented!")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def load_corpus(corpus_path: str):
|
|
31
|
+
corpus = datasets.load_dataset("json", data_files=corpus_path, split="train")
|
|
32
|
+
return corpus
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def read_jsonl(file_path):
|
|
36
|
+
with open(file_path, "r") as f:
|
|
37
|
+
while True:
|
|
38
|
+
new_line = f.readline()
|
|
39
|
+
if not new_line:
|
|
40
|
+
return
|
|
41
|
+
new_item = json.loads(new_line)
|
|
42
|
+
|
|
43
|
+
yield new_item
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def load_docs(corpus, doc_idxs):
|
|
47
|
+
results = [corpus[int(idx)] for idx in doc_idxs]
|
|
48
|
+
|
|
49
|
+
return results
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
OPENAI_MODEL_DICT = {
|
|
2
|
+
# chat
|
|
3
|
+
"gpt-4o": "o200k_base",
|
|
4
|
+
"gpt-4": "cl100k_base",
|
|
5
|
+
"gpt-3.5-turbo": "cl100k_base",
|
|
6
|
+
"gpt-3.5": "cl100k_base", # Common shorthand
|
|
7
|
+
"gpt-35-turbo": "cl100k_base", # Azure deployment name
|
|
8
|
+
# base
|
|
9
|
+
"davinci-002": "cl100k_base",
|
|
10
|
+
"babbage-002": "cl100k_base",
|
|
11
|
+
# embeddings
|
|
12
|
+
"text-embedding-ada-002": "cl100k_base",
|
|
13
|
+
"text-embedding-3-small": "cl100k_base",
|
|
14
|
+
"text-embedding-3-large": "cl100k_base",
|
|
15
|
+
# DEPRECATED MODELS
|
|
16
|
+
# text (DEPRECATED)
|
|
17
|
+
"text-davinci-003": "p50k_base",
|
|
18
|
+
"text-davinci-002": "p50k_base",
|
|
19
|
+
"text-davinci-001": "r50k_base",
|
|
20
|
+
"text-curie-001": "r50k_base",
|
|
21
|
+
"text-babbage-001": "r50k_base",
|
|
22
|
+
"text-ada-001": "r50k_base",
|
|
23
|
+
"davinci": "r50k_base",
|
|
24
|
+
"curie": "r50k_base",
|
|
25
|
+
"babbage": "r50k_base",
|
|
26
|
+
"ada": "r50k_base",
|
|
27
|
+
# code (DEPRECATED)
|
|
28
|
+
"code-davinci-002": "p50k_base",
|
|
29
|
+
"code-davinci-001": "p50k_base",
|
|
30
|
+
"code-cushman-002": "p50k_base",
|
|
31
|
+
"code-cushman-001": "p50k_base",
|
|
32
|
+
"davinci-codex": "p50k_base",
|
|
33
|
+
"cushman-codex": "p50k_base",
|
|
34
|
+
# edit (DEPRECATED)
|
|
35
|
+
"text-davinci-edit-001": "p50k_edit",
|
|
36
|
+
"code-davinci-edit-001": "p50k_edit",
|
|
37
|
+
# old embeddings (DEPRECATED)
|
|
38
|
+
"text-similarity-davinci-001": "r50k_base",
|
|
39
|
+
"text-similarity-curie-001": "r50k_base",
|
|
40
|
+
"text-similarity-babbage-001": "r50k_base",
|
|
41
|
+
"text-similarity-ada-001": "r50k_base",
|
|
42
|
+
"text-search-davinci-doc-001": "r50k_base",
|
|
43
|
+
"text-search-curie-doc-001": "r50k_base",
|
|
44
|
+
"text-search-babbage-doc-001": "r50k_base",
|
|
45
|
+
"text-search-ada-doc-001": "r50k_base",
|
|
46
|
+
"code-search-babbage-code-001": "r50k_base",
|
|
47
|
+
"code-search-ada-code-001": "r50k_base",
|
|
48
|
+
# open source
|
|
49
|
+
"gpt2": "gpt2",
|
|
50
|
+
"gpt-2": "gpt2", # Maintains consistency with gpt-4
|
|
51
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
def selfask_pred_parse(pred):
|
|
2
|
+
"""Parsing the prediction results of self-ask format."""
|
|
3
|
+
FINAL_ANSWER_PREFIX = "So the final answer is: "
|
|
4
|
+
|
|
5
|
+
lines = pred.split("\n")
|
|
6
|
+
answer = ""
|
|
7
|
+
for line in lines:
|
|
8
|
+
if FINAL_ANSWER_PREFIX in line:
|
|
9
|
+
answer = line.split(FINAL_ANSWER_PREFIX)[1].strip()
|
|
10
|
+
break
|
|
11
|
+
|
|
12
|
+
return answer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def ircot_pred_parse(pred):
|
|
16
|
+
FINAL_ANSWER_PREFIX = "So the answer is:"
|
|
17
|
+
if FINAL_ANSWER_PREFIX in pred:
|
|
18
|
+
answer = pred.split(FINAL_ANSWER_PREFIX)[1].strip()
|
|
19
|
+
else:
|
|
20
|
+
answer = pred
|
|
21
|
+
return answer
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def basic_pred_parse(pred):
|
|
25
|
+
return pred.split("\n")[0].strip()
|
flashrag/utils/utils.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import importlib
|
|
3
|
+
from transformers import AutoConfig
|
|
4
|
+
from flashrag.dataset.dataset import Dataset
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_dataset(config):
|
|
8
|
+
"""Load dataset from config."""
|
|
9
|
+
|
|
10
|
+
dataset_path = config["dataset_path"]
|
|
11
|
+
all_split = config["split"]
|
|
12
|
+
|
|
13
|
+
split_dict = {split: None for split in all_split}
|
|
14
|
+
|
|
15
|
+
for split in all_split:
|
|
16
|
+
split_path = os.path.join(dataset_path, f"{split}.jsonl")
|
|
17
|
+
if not os.path.exists(split_path):
|
|
18
|
+
print(f"{split} file not exists!")
|
|
19
|
+
continue
|
|
20
|
+
if split in ["test", "val", "dev"]:
|
|
21
|
+
split_dict[split] = Dataset(
|
|
22
|
+
config, split_path, sample_num=config["test_sample_num"], random_sample=config["random_sample"]
|
|
23
|
+
)
|
|
24
|
+
else:
|
|
25
|
+
split_dict[split] = Dataset(config, split_path)
|
|
26
|
+
|
|
27
|
+
return split_dict
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_generator(config, **params):
|
|
31
|
+
"""Automatically select generator class based on config."""
|
|
32
|
+
if config["framework"] == "vllm":
|
|
33
|
+
return getattr(importlib.import_module("flashrag.generator"), "VLLMGenerator")(config, **params)
|
|
34
|
+
elif config["framework"] == "fschat":
|
|
35
|
+
return getattr(importlib.import_module("flashrag.generator"), "FastChatGenerator")(config, **params)
|
|
36
|
+
elif config["framework"] == "hf":
|
|
37
|
+
model_config = AutoConfig.from_pretrained(config["generator_model_path"])
|
|
38
|
+
arch = model_config.architectures[0]
|
|
39
|
+
if "t5" in arch.lower() or "bart" in arch.lower():
|
|
40
|
+
return getattr(importlib.import_module("flashrag.generator"), "EncoderDecoderGenerator")(config, **params)
|
|
41
|
+
else:
|
|
42
|
+
return getattr(importlib.import_module("flashrag.generator"), "HFCausalLMGenerator")(config, **params)
|
|
43
|
+
elif config["framework"] == "openai":
|
|
44
|
+
return getattr(importlib.import_module("flashrag.generator"), "OpenaiGenerator")(config, **params)
|
|
45
|
+
else:
|
|
46
|
+
raise NotImplementedError
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_retriever(config):
|
|
50
|
+
r"""Automatically select retriever class based on config's retrieval method
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
config (dict): configuration with 'retrieval_method' key
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Retriever: retriever instance
|
|
57
|
+
"""
|
|
58
|
+
if config["retrieval_method"] == "bm25":
|
|
59
|
+
return getattr(importlib.import_module("flashrag.retriever"), "BM25Retriever")(config)
|
|
60
|
+
else:
|
|
61
|
+
return getattr(importlib.import_module("flashrag.retriever"), "DenseRetriever")(config)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_reranker(config):
|
|
65
|
+
model_path = config["rerank_model_path"]
|
|
66
|
+
# get model config
|
|
67
|
+
model_config = AutoConfig.from_pretrained(model_path)
|
|
68
|
+
arch = model_config.architectures[0]
|
|
69
|
+
if "forsequenceclassification" in arch.lower():
|
|
70
|
+
return getattr(importlib.import_module("flashrag.retriever"), "CrossReranker")(config)
|
|
71
|
+
else:
|
|
72
|
+
return getattr(importlib.import_module("flashrag.retriever"), "BiReranker")(config)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_judger(config):
|
|
76
|
+
judger_name = config["judger_name"]
|
|
77
|
+
if "skr" in judger_name.lower():
|
|
78
|
+
return getattr(importlib.import_module("flashrag.judger"), "SKRJudger")(config)
|
|
79
|
+
elif "adaptive" in judger_name.lower():
|
|
80
|
+
return getattr(importlib.import_module("flashrag.judger"), "AdaptiveJudger")(config)
|
|
81
|
+
else:
|
|
82
|
+
assert False, "No implementation!"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_refiner(config, retriever=None, generator=None):
|
|
86
|
+
# 预定义默认路径字典
|
|
87
|
+
DEFAULT_PATH_DICT = {
|
|
88
|
+
"recomp_abstractive_nq": "fangyuan/nq_abstractive_compressor",
|
|
89
|
+
"recomp:abstractive_tqa": "fangyuan/tqa_abstractive_compressor",
|
|
90
|
+
"recomp:abstractive_hotpotqa": "fangyuan/hotpotqa_abstractive",
|
|
91
|
+
}
|
|
92
|
+
REFINER_MODULE = importlib.import_module("flashrag.refiner")
|
|
93
|
+
|
|
94
|
+
refiner_name = config["refiner_name"]
|
|
95
|
+
refiner_path = (
|
|
96
|
+
config["refiner_model_path"]
|
|
97
|
+
if config["refiner_model_path"] is not None
|
|
98
|
+
else DEFAULT_PATH_DICT.get(refiner_name, None)
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
model_config = AutoConfig.from_pretrained(refiner_path)
|
|
103
|
+
arch = model_config.architectures[0].lower()
|
|
104
|
+
except:
|
|
105
|
+
model_config, arch = "", ""
|
|
106
|
+
|
|
107
|
+
if "recomp" in refiner_name or "bert" in arch:
|
|
108
|
+
if model_config.model_type == "t5":
|
|
109
|
+
refiner_class = "AbstractiveRecompRefiner"
|
|
110
|
+
else:
|
|
111
|
+
refiner_class = "ExtractiveRefiner"
|
|
112
|
+
elif "lingua" in refiner_name:
|
|
113
|
+
refiner_class = "LLMLinguaRefiner"
|
|
114
|
+
elif "selective-context" in refiner_name or "sc" in refiner_name:
|
|
115
|
+
refiner_class = "SelectiveContextRefiner"
|
|
116
|
+
elif "kg-trace" in refiner_name:
|
|
117
|
+
return getattr(REFINER_MODULE, "KGTraceRefiner")(config, retriever, generator)
|
|
118
|
+
else:
|
|
119
|
+
raise ValueError("No implementation!")
|
|
120
|
+
|
|
121
|
+
return getattr(REFINER_MODULE, refiner_class)(config)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def hash_object(o) -> str:
|
|
125
|
+
"""Returns a character hash code of arbitrary Python objects."""
|
|
126
|
+
import hashlib
|
|
127
|
+
import io
|
|
128
|
+
import dill
|
|
129
|
+
import base58
|
|
130
|
+
|
|
131
|
+
m = hashlib.blake2b()
|
|
132
|
+
with io.BytesIO() as buffer:
|
|
133
|
+
dill.dump(o, buffer)
|
|
134
|
+
m.update(buffer.getbuffer())
|
|
135
|
+
return base58.b58encode(m.digest()).decode()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Jiajie Jin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|