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,148 @@
1
+ import os
2
+ import pathlib
3
+ from typing import List, Dict, Union
4
+
5
+ import pandas as pd
6
+
7
+ from autorag.evaluation import evaluate_generation
8
+ from autorag.evaluation.util import cast_metrics
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 to_list
12
+
13
+
14
+ def run_generator_node(
15
+ modules: List,
16
+ module_params: List[Dict],
17
+ previous_result: pd.DataFrame,
18
+ node_line_dir: str,
19
+ strategies: Dict,
20
+ ) -> pd.DataFrame:
21
+ """
22
+ Run evaluation and select the best module among generator node results.
23
+ And save the results and summary to generator node directory.
24
+
25
+ :param modules: Generator modules to run.
26
+ :param module_params: Generator module parameters.
27
+ Including node parameters, which is used for every module in this node.
28
+ :param previous_result: Previous result dataframe.
29
+ Could be prompt maker node's result.
30
+ :param node_line_dir: This node line's directory.
31
+ :param strategies: Strategies for generator node.
32
+ :return: The best result dataframe.
33
+ It contains previous result columns and generator node's result columns.
34
+ """
35
+ if not os.path.exists(node_line_dir):
36
+ os.makedirs(node_line_dir)
37
+ project_dir = pathlib.PurePath(node_line_dir).parent.parent
38
+ node_dir = os.path.join(node_line_dir, "generator") # node name
39
+ if not os.path.exists(node_dir):
40
+ os.makedirs(node_dir)
41
+ qa_data = pd.read_parquet(
42
+ os.path.join(project_dir, "data", "qa.parquet"), engine="pyarrow"
43
+ )
44
+ if "generation_gt" not in qa_data.columns:
45
+ raise ValueError("You must have 'generation_gt' column in qa.parquet.")
46
+
47
+ results, execution_times = zip(
48
+ *map(
49
+ lambda x: measure_speed(
50
+ x[0].run_evaluator,
51
+ project_dir=project_dir,
52
+ previous_result=previous_result,
53
+ **x[1],
54
+ ),
55
+ zip(modules, module_params),
56
+ )
57
+ )
58
+ average_times = list(map(lambda x: x / len(results[0]), execution_times))
59
+
60
+ # get average token usage
61
+ token_usages = list(map(lambda x: x["generated_tokens"].apply(len).mean(), results))
62
+
63
+ # make rows to metric_inputs
64
+ generation_gt = to_list(qa_data["generation_gt"].tolist())
65
+ queries = to_list(qa_data["query"].tolist())
66
+
67
+ metric_inputs = [
68
+ MetricInput(generation_gt=gen_gt, query=query)
69
+ for gen_gt, query in zip(generation_gt, queries)
70
+ ]
71
+
72
+ metric_names, metric_params = cast_metrics(strategies.get("metrics"))
73
+ if metric_names is None or len(metric_names) <= 0:
74
+ raise ValueError("You must at least one metrics for generator evaluation.")
75
+ results = list(
76
+ map(
77
+ lambda result: evaluate_generator_node(
78
+ result, metric_inputs, strategies.get("metrics")
79
+ ),
80
+ results,
81
+ )
82
+ )
83
+
84
+ # save results to folder
85
+ filepaths = list(
86
+ map(lambda x: os.path.join(node_dir, f"{x}.parquet"), range(len(modules)))
87
+ )
88
+ list(
89
+ map(lambda x: x[0].to_parquet(x[1], index=False), zip(results, filepaths))
90
+ ) # execute save to parquet
91
+ filenames = list(map(lambda x: os.path.basename(x), filepaths))
92
+
93
+ summary_df = pd.DataFrame(
94
+ {
95
+ "filename": filenames,
96
+ "module_name": list(map(lambda module: module.__name__, modules)),
97
+ "module_params": module_params,
98
+ "execution_time": average_times,
99
+ "average_output_token": token_usages,
100
+ **{
101
+ metric: list(map(lambda x: x[metric].mean(), results))
102
+ for metric in metric_names
103
+ },
104
+ }
105
+ )
106
+
107
+ # filter by strategies
108
+ if strategies.get("speed_threshold") is not None:
109
+ results, filenames = filter_by_threshold(
110
+ results, average_times, strategies["speed_threshold"], filenames
111
+ )
112
+ if strategies.get("token_threshold") is not None:
113
+ results, filenames = filter_by_threshold(
114
+ results, token_usages, strategies["token_threshold"], filenames
115
+ )
116
+ selected_result, selected_filename = select_best(
117
+ results, metric_names, filenames, strategies.get("strategy", "mean")
118
+ )
119
+ best_result = pd.concat([previous_result, selected_result], axis=1)
120
+
121
+ # add 'is_best' column at summary file
122
+ summary_df["is_best"] = summary_df["filename"] == selected_filename
123
+
124
+ # save files
125
+ summary_df.to_csv(os.path.join(node_dir, "summary.csv"), index=False)
126
+ best_result.to_parquet(
127
+ os.path.join(
128
+ node_dir, f"best_{os.path.splitext(selected_filename)[0]}.parquet"
129
+ ),
130
+ index=False,
131
+ )
132
+ return best_result
133
+
134
+
135
+ def evaluate_generator_node(
136
+ result_df: pd.DataFrame,
137
+ metric_inputs: List[MetricInput],
138
+ metrics: Union[List[str], List[Dict]],
139
+ ):
140
+ @evaluate_generation(metric_inputs=metric_inputs, metrics=metrics)
141
+ def evaluate_generation_module(df: pd.DataFrame):
142
+ return (
143
+ df["generated_texts"].tolist(),
144
+ df["generated_tokens"].tolist(),
145
+ df["generated_log_probs"].tolist(),
146
+ )
147
+
148
+ return evaluate_generation_module(result_df)
@@ -0,0 +1,147 @@
1
+ import gc
2
+ from copy import deepcopy
3
+ from typing import List, Tuple, Union
4
+
5
+ import pandas as pd
6
+
7
+ from autorag.nodes.generator.base import BaseGenerator
8
+ from autorag.utils import result_to_dataframe
9
+ from autorag.utils.util import pop_params, to_list, is_chat_prompt
10
+
11
+
12
+ class Vllm(BaseGenerator):
13
+ def __init__(self, project_dir: str, llm: str, **kwargs):
14
+ super().__init__(project_dir, llm, **kwargs)
15
+ try:
16
+ from vllm import SamplingParams, LLM
17
+ except ImportError:
18
+ raise ImportError(
19
+ "Please install vllm library. You can install it by running `pip install vllm`."
20
+ )
21
+
22
+ model_from_kwargs = kwargs.pop("model", None)
23
+ model = llm if model_from_kwargs is None else model_from_kwargs
24
+
25
+ input_kwargs = deepcopy(kwargs)
26
+ sampling_params_init_params = pop_params(
27
+ SamplingParams.from_optional, input_kwargs
28
+ )
29
+ input_kwargs.pop("thinking", None)
30
+ self.vllm_model = LLM(model, **input_kwargs)
31
+
32
+ # delete not sampling param keys in the kwargs
33
+ kwargs_keys = list(kwargs.keys())
34
+ for key in kwargs_keys:
35
+ if key not in sampling_params_init_params:
36
+ kwargs.pop(key)
37
+
38
+ def __del__(self):
39
+ try:
40
+ import torch
41
+ import contextlib
42
+
43
+ if torch.cuda.is_available():
44
+ from vllm.distributed.parallel_state import (
45
+ destroy_model_parallel,
46
+ destroy_distributed_environment,
47
+ )
48
+
49
+ destroy_model_parallel()
50
+ destroy_distributed_environment()
51
+ if hasattr(self.vllm_model.llm_engine, "model_executor"):
52
+ del self.vllm_model.llm_engine.model_executor
53
+ del self.vllm_model
54
+ with contextlib.suppress(AssertionError):
55
+ torch.distributed.destroy_process_group()
56
+ gc.collect()
57
+ torch.cuda.empty_cache()
58
+ torch.cuda.synchronize()
59
+ except ImportError:
60
+ del self.vllm_model
61
+
62
+ super().__del__()
63
+
64
+ @result_to_dataframe(["generated_texts", "generated_tokens", "generated_log_probs"])
65
+ def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
66
+ prompts = self.cast_to_run(previous_result)
67
+ thinking = kwargs.pop("thinking", False)
68
+ return self._pure(prompts, thinking=thinking, **kwargs)
69
+
70
+ def _pure(
71
+ self,
72
+ prompts: Union[List[str], List[List[dict]]],
73
+ thinking: bool = False,
74
+ **kwargs,
75
+ ) -> Tuple[List[str], List[List[int]], List[List[float]]]:
76
+ """
77
+ Vllm module.
78
+ It gets the VLLM instance and returns generated texts by the input prompt.
79
+ You can set logprobs to get the log probs of the generated text.
80
+ Default logprobs is 1.
81
+
82
+ :param prompts: A list of prompts or a list of chat prompts.
83
+ :param thinking: A boolean that indicates whether to think when generating text.
84
+ Default is False.
85
+ Effective when set True and using chat prompts.
86
+ You can learn how to use chat prompt at `chat_fstring` module documentation.
87
+ :param kwargs: The extra parameters for generating the text.
88
+ :return: A tuple of three elements.
89
+ The first element is a list of generated text.
90
+ The second element is a list of generated text's token ids.
91
+ The third element is a list of generated text's log probs.
92
+ """
93
+ try:
94
+ from vllm.outputs import RequestOutput
95
+ from vllm import SamplingParams, LLM
96
+
97
+ try:
98
+ from vllm.logprobs import SampleLogprobs # vLLM >= 0.11
99
+ except ImportError: # pragma: no cover - older vLLM fallback
100
+ from vllm.sequence import SampleLogprobs
101
+ except ImportError:
102
+ raise ImportError(
103
+ "Please install vllm library. You can install it by running `pip install vllm`."
104
+ )
105
+
106
+ if "logprobs" not in kwargs:
107
+ kwargs["logprobs"] = 1
108
+
109
+ sampling_params = pop_params(SamplingParams.from_optional, kwargs)
110
+ generate_params = SamplingParams(**sampling_params)
111
+ if is_chat_prompt(prompts):
112
+ chat_template_kwargs = kwargs.pop("chat_template_kwargs", {})
113
+ chat_template_kwargs["enable_thinking"] = thinking
114
+ chat_kwargs = pop_params(LLM.chat, kwargs)
115
+ results: List[RequestOutput] = self.vllm_model.chat(
116
+ prompts,
117
+ generate_params,
118
+ chat_template_kwargs=chat_template_kwargs,
119
+ **chat_kwargs,
120
+ )
121
+ else:
122
+ generate_kwargs = pop_params(LLM.generate, kwargs)
123
+ results: List[RequestOutput] = self.vllm_model.generate(
124
+ prompts, generate_params, **generate_kwargs
125
+ )
126
+ generated_texts = list(map(lambda x: x.outputs[0].text, results))
127
+ generated_token_ids = list(map(lambda x: x.outputs[0].token_ids, results))
128
+ log_probs: List[SampleLogprobs] = list(
129
+ map(lambda x: x.outputs[0].logprobs, results)
130
+ )
131
+ generated_log_probs = list(
132
+ map(
133
+ lambda x: list(map(lambda y: y[0][y[1]].logprob, zip(x[0], x[1]))),
134
+ zip(log_probs, generated_token_ids),
135
+ )
136
+ )
137
+ return (
138
+ to_list(generated_texts),
139
+ to_list(generated_token_ids),
140
+ to_list(generated_log_probs),
141
+ )
142
+
143
+ async def astream(self, prompt: str, **kwargs):
144
+ raise NotImplementedError
145
+
146
+ def stream(self, prompt: str, **kwargs):
147
+ raise NotImplementedError
@@ -0,0 +1,191 @@
1
+ import logging
2
+ from typing import List, Tuple, Dict, Union
3
+ import time
4
+
5
+ import pandas as pd
6
+ import requests
7
+ from asyncio import to_thread
8
+
9
+ from autorag.nodes.generator.base import BaseGenerator
10
+ from autorag.utils.util import get_event_loop, process_batch, result_to_dataframe
11
+
12
+ logger = logging.getLogger("AutoRAG")
13
+
14
+ DEFAULT_MAX_TOKENS = 4096 # Default token limit
15
+
16
+
17
+ class VllmAPI(BaseGenerator):
18
+ def __init__(
19
+ self,
20
+ project_dir,
21
+ llm: str,
22
+ uri: str,
23
+ max_tokens: int = None,
24
+ batch: int = 16,
25
+ *args,
26
+ **kwargs,
27
+ ):
28
+ """
29
+ VLLM API Wrapper for OpenAI-compatible chat/completions format.
30
+
31
+ :param project_dir: Project directory.
32
+ :param llm: Model name (e.g., LLaMA model).
33
+ :param uri: VLLM API server URI.
34
+ :param max_tokens: Maximum token limit.
35
+ Default is 4096.
36
+ :param batch: Request batch size.
37
+ Default is 16.
38
+ """
39
+ super().__init__(project_dir, llm, *args, **kwargs)
40
+ assert batch > 0, "Batch size must be greater than 0."
41
+ self.uri = uri.rstrip("/") # Set API URI
42
+ self.batch = batch
43
+ # Use the provided max_tokens if available, otherwise use the default
44
+ self.max_token_size = max_tokens if max_tokens else DEFAULT_MAX_TOKENS
45
+ self.max_model_len = self.get_max_model_length()
46
+ logger.info(f"{llm} max model length: {self.max_model_len}")
47
+
48
+ @result_to_dataframe(["generated_texts", "generated_tokens", "generated_log_probs"])
49
+ def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
50
+ prompts = self.cast_to_run(previous_result)
51
+ return self._pure(prompts, **kwargs)
52
+
53
+ def _pure(
54
+ self,
55
+ prompts: Union[List[str], List[List[Dict]]],
56
+ truncate: bool = True,
57
+ **kwargs,
58
+ ) -> Tuple[List[str], List[List[int]], List[List[float]]]:
59
+ """
60
+ Method to call the VLLM API to generate text.
61
+
62
+ :param prompts: List of input prompts.
63
+ :param truncate: Whether to truncate input prompts to fit within the token limit.
64
+ :param kwargs: Additional options (e.g., temperature, top_p).
65
+ :return: Generated text, token lists, and log probability lists.
66
+ """
67
+ if kwargs.get("logprobs") is not None:
68
+ kwargs.pop("logprobs")
69
+ logger.warning(
70
+ "parameter logprob does not effective. It always set to True."
71
+ )
72
+ if kwargs.get("n") is not None:
73
+ kwargs.pop("n")
74
+ logger.warning("parameter n does not effective. It always set to 1.")
75
+
76
+ if truncate:
77
+ prompts = list(map(lambda p: self.truncate_by_token(p), prompts))
78
+ loop = get_event_loop()
79
+ tasks = [to_thread(self.get_result, prompt, **kwargs) for prompt in prompts]
80
+ results = loop.run_until_complete(process_batch(tasks, self.batch))
81
+
82
+ answer_result = list(map(lambda x: x[0], results))
83
+ token_result = list(map(lambda x: x[1], results))
84
+ logprob_result = list(map(lambda x: x[2], results))
85
+ return answer_result, token_result, logprob_result
86
+
87
+ def truncate_by_token(self, prompt: Union[str, List[Dict]]) -> str:
88
+ """
89
+ Function to truncate prompts to fit within the maximum token limit.
90
+ """
91
+ if not isinstance(prompt, str):
92
+ content_list = [msg["content"] for msg in prompt]
93
+ prompt = "\n".join(content_list)
94
+ tokens = self.encoding_for_model(prompt)["tokens"] # Simple tokenization
95
+ return self.decoding_for_model(tokens[: self.max_model_len])["prompt"]
96
+
97
+ def call_vllm_api(self, prompt: Union[str, List[Dict]], **kwargs) -> dict:
98
+ """
99
+ Calls the VLLM API to get chat/completions responses.
100
+
101
+ :param prompt: Input prompt.
102
+ :param kwargs: Additional API options (e.g., temperature, max_tokens).
103
+ :return: API response.
104
+ """
105
+ payload = {
106
+ "model": self.llm,
107
+ "messages": parse_prompt(prompt),
108
+ "temperature": kwargs.get("temperature", 0.4),
109
+ "max_tokens": min(
110
+ kwargs.get("max_tokens", self.max_token_size), self.max_token_size
111
+ ),
112
+ "logprobs": True,
113
+ "n": 1,
114
+ }
115
+ start_time = time.time() # Record request start time
116
+ response = requests.post(f"{self.uri}/v1/chat/completions", json=payload)
117
+ end_time = time.time() # Record request end time
118
+
119
+ response.raise_for_status()
120
+ elapsed_time = end_time - start_time # Calculate elapsed time
121
+ logger.info(
122
+ f"Request chat completions to vllm server completed in {elapsed_time:.2f} seconds"
123
+ )
124
+ return response.json()
125
+
126
+ # Additional method: abstract method implementation
127
+ async def astream(self, prompt: Union[str, List[Dict]], **kwargs):
128
+ """
129
+ Asynchronous streaming method not implemented.
130
+ """
131
+ raise NotImplementedError("astream method is not implemented for VLLM API yet.")
132
+
133
+ def stream(self, prompt: Union[str, List[Dict]], **kwargs):
134
+ """
135
+ Synchronous streaming method not implemented.
136
+ """
137
+ raise NotImplementedError("stream method is not implemented for VLLM API yet.")
138
+
139
+ def get_result(self, prompt: Union[str, List[Dict]], **kwargs):
140
+ response = self.call_vllm_api(prompt, **kwargs)
141
+ choice = response["choices"][0]
142
+ answer = choice["message"]["content"]
143
+
144
+ # Handle cases where logprobs is None
145
+ if choice.get("logprobs") and "content" in choice["logprobs"]:
146
+ logprobs = list(map(lambda x: x["logprob"], choice["logprobs"]["content"]))
147
+ tokens = list(
148
+ map(
149
+ lambda x: self.encoding_for_model(x["token"])["tokens"],
150
+ choice["logprobs"]["content"],
151
+ )
152
+ )
153
+ else:
154
+ logprobs = []
155
+ tokens = []
156
+
157
+ return answer, tokens, logprobs
158
+
159
+ def encoding_for_model(self, answer_piece: str):
160
+ payload = {
161
+ "model": self.llm,
162
+ "prompt": answer_piece,
163
+ "add_special_tokens": True,
164
+ }
165
+ response = requests.post(f"{self.uri}/tokenize", json=payload)
166
+ response.raise_for_status()
167
+ return response.json()
168
+
169
+ def decoding_for_model(self, tokens: list[int]):
170
+ payload = {
171
+ "model": self.llm,
172
+ "tokens": tokens,
173
+ }
174
+ response = requests.post(f"{self.uri}/detokenize", json=payload)
175
+ response.raise_for_status()
176
+ return response.json()
177
+
178
+ def get_max_model_length(self):
179
+ response = requests.get(f"{self.uri}/v1/models")
180
+ response.raise_for_status()
181
+ json_data = response.json()
182
+ return json_data["data"][0]["max_model_len"]
183
+
184
+
185
+ def parse_prompt(prompt: Union[str, List[Dict]]) -> List[Dict]:
186
+ if isinstance(prompt, str):
187
+ return [{"role": "user", "content": prompt}]
188
+ elif isinstance(prompt, list):
189
+ return prompt
190
+ else:
191
+ raise ValueError("prompt must be a string or a list of dicts.")
@@ -0,0 +1,2 @@
1
+ from .hybrid_cc import HybridCC
2
+ from .hybrid_rrf import HybridRRF
@@ -0,0 +1,58 @@
1
+ import abc
2
+
3
+ import pandas as pd
4
+
5
+ from autorag.nodes.retrieval.base import BaseRetrieval
6
+ from autorag.utils import result_to_dataframe
7
+ from autorag.utils.util import pop_params, fetch_contents
8
+
9
+
10
+ class HybridRetrieval(BaseRetrieval, metaclass=abc.ABCMeta):
11
+ def __init__(self, project_dir: str, *args, **kwargs):
12
+ super().__init__(project_dir)
13
+
14
+ @result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
15
+ def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
16
+ previous_info = self.cast_to_run(previous_result, *args, **kwargs)
17
+ _pure_params = pop_params(self._pure, kwargs)
18
+ ids, scores = self._pure(previous_info, **_pure_params)
19
+ contents = fetch_contents(self.corpus_df, ids)
20
+ return contents, ids, scores
21
+
22
+ def cast_to_run(self, previous_result: pd.DataFrame, *args, **kwargs):
23
+ return hybrid_cast(previous_result)
24
+
25
+ @classmethod
26
+ def cast_to_run_class(cls, previous_result: pd.DataFrame):
27
+ return hybrid_cast(previous_result)
28
+
29
+
30
+ def hybrid_cast(
31
+ previous_result: pd.DataFrame,
32
+ ):
33
+ assert "query" in previous_result.columns, "previous_result must have query column."
34
+ queries = previous_result["query"].tolist()
35
+
36
+ assert "retrieved_contents_semantic" in previous_result.columns
37
+ assert "retrieved_contents_lexical" in previous_result.columns
38
+ assert "retrieve_scores_semantic" in previous_result.columns
39
+ assert "retrieve_scores_lexical" in previous_result.columns
40
+ assert "retrieved_ids_semantic" in previous_result.columns
41
+ assert "retrieved_ids_lexical" in previous_result.columns
42
+
43
+ contents_semantic = previous_result["retrieved_contents_semantic"].tolist()
44
+ contents_lexical = previous_result["retrieved_contents_lexical"].tolist()
45
+ scores_semantic = previous_result["retrieve_scores_semantic"].tolist()
46
+ scores_lexical = previous_result["retrieve_scores_lexical"].tolist()
47
+ ids_semantic = previous_result["retrieved_ids_semantic"].tolist()
48
+ ids_lexical = previous_result["retrieved_ids_lexical"].tolist()
49
+
50
+ return {
51
+ "queries": queries,
52
+ "retrieved_contents_semantic": contents_semantic,
53
+ "retrieved_contents_lexical": contents_lexical,
54
+ "retrieve_scores_semantic": scores_semantic,
55
+ "retrieve_scores_lexical": scores_lexical,
56
+ "retrieved_ids_semantic": ids_semantic,
57
+ "retrieved_ids_lexical": ids_lexical,
58
+ }