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
autorag/__init__.py ADDED
@@ -0,0 +1,82 @@
1
+ import logging
2
+ import sys
3
+ from typing import Any
4
+
5
+ from llama_index.core.base.llms.types import CompletionResponse
6
+ from llama_index.core.llms.mock import MockLLM
7
+ from llama_index.llms.bedrock import Bedrock
8
+
9
+ from llama_index.llms.openai import OpenAI
10
+ from llama_index.llms.openai_like import OpenAILike
11
+ from rich.logging import RichHandler
12
+
13
+
14
+ class LazyInit:
15
+ def __init__(self, factory, *args, **kwargs):
16
+ self._factory = factory
17
+ self._args = args
18
+ self._kwargs = kwargs
19
+ self._instance = None
20
+
21
+ def __call__(self):
22
+ if self._instance is None:
23
+ self._instance = self._factory(*self._args, **self._kwargs)
24
+ return self._instance
25
+
26
+ def __getattr__(self, name):
27
+ if self._instance is None:
28
+ self._instance = self._factory(*self._args, **self._kwargs)
29
+ return getattr(self._instance, name)
30
+
31
+
32
+ rich_format = "[%(filename)s:%(lineno)s] >> %(message)s"
33
+ logging.basicConfig(
34
+ level="INFO", format=rich_format, handlers=[RichHandler(rich_tracebacks=True)]
35
+ )
36
+ logger = logging.getLogger("AutoRAG")
37
+
38
+
39
+ def handle_exception(exc_type, exc_value, exc_traceback):
40
+ logger = logging.getLogger("AutoRAG")
41
+ logger.error("Unexpected exception", exc_info=(exc_type, exc_value, exc_traceback))
42
+
43
+
44
+ sys.excepthook = handle_exception
45
+
46
+
47
+ class AutoRAGBedrock(Bedrock):
48
+ async def acomplete(
49
+ self, prompt: str, formatted: bool = False, **kwargs: Any
50
+ ) -> CompletionResponse:
51
+ return self.complete(prompt, formatted=formatted, **kwargs)
52
+
53
+
54
+ generator_models = {
55
+ "openai": OpenAI,
56
+ "openailike": OpenAILike,
57
+ "mock": MockLLM,
58
+ "bedrock": AutoRAGBedrock,
59
+ }
60
+
61
+ try:
62
+ from llama_index.llms.huggingface import HuggingFaceLLM
63
+ from llama_index.llms.ollama import Ollama
64
+
65
+ generator_models["huggingfacellm"] = HuggingFaceLLM
66
+ generator_models["ollama"] = Ollama
67
+
68
+ except ImportError:
69
+ logger.info(
70
+ "You are using API version of AutoRAG."
71
+ "To use local version, run pip install 'AutoRAG[gpu]'"
72
+ )
73
+
74
+ try:
75
+ import transformers
76
+
77
+ transformers.logging.set_verbosity_error()
78
+ except ImportError:
79
+ logger.info(
80
+ "You are using API version of AutoRAG."
81
+ "To use local version, run pip install 'AutoRAG[gpu]'"
82
+ )
autorag/chunker.py ADDED
@@ -0,0 +1,51 @@
1
+ import logging
2
+ import os
3
+ import shutil
4
+ from typing import Optional
5
+
6
+ import pandas as pd
7
+
8
+ from autorag.data.chunk.run import run_chunker
9
+ from autorag.data.utils.util import load_yaml, get_param_combinations
10
+
11
+ logger = logging.getLogger("AutoRAG")
12
+
13
+
14
+ class Chunker:
15
+ def __init__(self, raw_df: pd.DataFrame, project_dir: Optional[str] = None):
16
+ self.parsed_raw = raw_df
17
+ self.project_dir = project_dir if project_dir is not None else os.getcwd()
18
+
19
+ @classmethod
20
+ def from_parquet(
21
+ cls, parsed_data_path: str, project_dir: Optional[str] = None
22
+ ) -> "Chunker":
23
+ if not os.path.exists(parsed_data_path):
24
+ raise ValueError(f"parsed_data_path {parsed_data_path} does not exist.")
25
+ if not parsed_data_path.endswith("parquet"):
26
+ raise ValueError(
27
+ f"parsed_data_path {parsed_data_path} is not a parquet file."
28
+ )
29
+ parsed_result = pd.read_parquet(parsed_data_path, engine="pyarrow")
30
+ return cls(parsed_result, project_dir)
31
+
32
+ def start_chunking(self, yaml_path: str):
33
+ if not os.path.exists(self.project_dir):
34
+ os.makedirs(self.project_dir)
35
+
36
+ # Copy YAML file to the trial directory
37
+ shutil.copy(yaml_path, os.path.join(self.project_dir, "chunk_config.yaml"))
38
+
39
+ # load yaml file
40
+ modules = load_yaml(yaml_path)
41
+
42
+ input_modules, input_params = get_param_combinations(modules)
43
+
44
+ logger.info("Chunking Start...")
45
+ run_chunker(
46
+ modules=input_modules,
47
+ module_params=input_params,
48
+ parsed_result=self.parsed_raw,
49
+ project_dir=self.project_dir,
50
+ )
51
+ logger.info("Chunking Done!")
autorag/cli.py ADDED
@@ -0,0 +1,209 @@
1
+ import importlib.resources
2
+ import logging
3
+ import os
4
+ import pathlib
5
+ import subprocess
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ import click
10
+ import nest_asyncio
11
+
12
+ from autorag import dashboard
13
+ from autorag.deploy import extract_best_config as original_extract_best_config
14
+ from autorag.deploy.api import ApiRunner
15
+ from autorag.evaluator import Evaluator
16
+ from autorag.validator import Validator
17
+
18
+ logger = logging.getLogger("AutoRAG")
19
+
20
+ autorag_dir = os.path.dirname(os.path.realpath(__file__))
21
+ version_file = os.path.join(autorag_dir, "VERSION")
22
+ with open(version_file, "r") as f:
23
+ __version__ = f.read().strip()
24
+
25
+
26
+ @click.group()
27
+ @click.version_option(__version__)
28
+ def cli():
29
+ pass
30
+
31
+
32
+ @click.command()
33
+ @click.option(
34
+ "--config",
35
+ "-c",
36
+ help="Path to config yaml file. Must be yaml or yml file.",
37
+ type=str,
38
+ )
39
+ @click.option(
40
+ "--qa_data_path", help="Path to QA dataset. Must be parquet file.", type=str
41
+ )
42
+ @click.option(
43
+ "--corpus_data_path", help="Path to corpus dataset. Must be parquet file.", type=str
44
+ )
45
+ @click.option(
46
+ "--project_dir", help="Path to project directory.", type=str, default=None
47
+ )
48
+ @click.option(
49
+ "--skip_validation",
50
+ help="Skip validation or not. Default is False.",
51
+ type=bool,
52
+ default=False,
53
+ )
54
+ def evaluate(config, qa_data_path, corpus_data_path, project_dir, skip_validation):
55
+ if not config.endswith(".yaml") and not config.endswith(".yml"):
56
+ raise ValueError(f"Config file {config} is not a yaml or yml file.")
57
+ if not os.path.exists(config):
58
+ raise ValueError(f"Config file {config} does not exist.")
59
+ evaluator = Evaluator(qa_data_path, corpus_data_path, project_dir=project_dir)
60
+ evaluator.start_trial(config, skip_validation=skip_validation)
61
+
62
+
63
+ @click.command()
64
+ @click.option(
65
+ "--config_path", type=str, help="Path to extracted config yaml file.", default=None
66
+ )
67
+ @click.option("--host", type=str, default="0.0.0.0", help="Host address")
68
+ @click.option("--port", type=int, default=8000, help="Port number")
69
+ @click.option(
70
+ "--trial_dir",
71
+ type=click.Path(file_okay=False, dir_okay=True, exists=True),
72
+ default=None,
73
+ help="Path to trial directory.",
74
+ )
75
+ @click.option(
76
+ "--project_dir", help="Path to project directory.", type=str, default=None
77
+ )
78
+ @click.option(
79
+ "--remote", help="Run the API server in remote mode.", type=bool, default=False
80
+ )
81
+ def run_api(config_path, host, port, trial_dir, project_dir, remote: bool):
82
+ if trial_dir is None:
83
+ runner = ApiRunner.from_yaml(config_path, project_dir=project_dir)
84
+ else:
85
+ runner = ApiRunner.from_trial_folder(trial_dir)
86
+ logger.info(f"Running API server at {host}:{port}...")
87
+ nest_asyncio.apply()
88
+ runner.run_api_server(host, port, remote=remote)
89
+
90
+
91
+ @click.command()
92
+ @click.option(
93
+ "--yaml_path", type=click.Path(path_type=Path), help="Path to the YAML file."
94
+ )
95
+ @click.option(
96
+ "--project_dir",
97
+ type=click.Path(path_type=Path),
98
+ help="Path to the project directory.",
99
+ )
100
+ @click.option(
101
+ "--trial_path", type=click.Path(path_type=Path), help="Path to the trial directory."
102
+ )
103
+ def run_web(
104
+ yaml_path: Optional[str], project_dir: Optional[str], trial_path: Optional[str]
105
+ ):
106
+ try:
107
+ with importlib.resources.path("autorag", "web.py") as web_path:
108
+ web_py_path = str(web_path)
109
+ except ImportError:
110
+ raise ImportError(
111
+ "Could not locate the web.py file within the autorag package."
112
+ " Please ensure that autorag is correctly installed."
113
+ )
114
+
115
+ if not yaml_path and not trial_path:
116
+ raise ValueError("yaml_path or trial_path must be given.")
117
+ elif yaml_path and trial_path:
118
+ raise ValueError("yaml_path and trial_path cannot be given at the same time.")
119
+ elif yaml_path and not project_dir:
120
+ subprocess.run(
121
+ ["streamlit", "run", web_py_path, "--", "--yaml_path", yaml_path]
122
+ )
123
+ elif yaml_path and project_dir:
124
+ subprocess.run(
125
+ [
126
+ "streamlit",
127
+ "run",
128
+ web_py_path,
129
+ "--",
130
+ "--yaml_path",
131
+ yaml_path,
132
+ "--project_dir",
133
+ project_dir,
134
+ ]
135
+ )
136
+ elif trial_path:
137
+ subprocess.run(
138
+ ["streamlit", "run", web_py_path, "--", "--trial_path", trial_path]
139
+ )
140
+
141
+
142
+ @click.command()
143
+ @click.option(
144
+ "--trial_dir",
145
+ type=click.Path(dir_okay=True, file_okay=False, exists=True),
146
+ required=True,
147
+ )
148
+ @click.option(
149
+ "--port", type=int, default=7690, help="Port number. The default is 7690."
150
+ )
151
+ def run_dashboard(trial_dir: str, port: int):
152
+ dashboard.run(trial_dir, port=port)
153
+
154
+
155
+ @click.command()
156
+ @click.option("--trial_path", type=click.Path(), help="Path to the trial directory.")
157
+ @click.option(
158
+ "--output_path",
159
+ type=click.Path(),
160
+ help="Path to the output directory. Must be .yaml or .yml file.",
161
+ )
162
+ def extract_best_config(trial_path: str, output_path: str):
163
+ original_extract_best_config(trial_path, output_path)
164
+
165
+
166
+ @click.command()
167
+ @click.option("--trial_path", help="Path to trial directory.", type=str)
168
+ def restart_evaluate(trial_path):
169
+ if not os.path.exists(trial_path):
170
+ raise ValueError(f"trial_path {trial_path} does not exist.")
171
+ project_dir = str(pathlib.PurePath(trial_path).parent)
172
+ qa_data_path = os.path.join(project_dir, "data", "qa.parquet")
173
+ corpus_data_path = os.path.join(project_dir, "data", "corpus.parquet")
174
+ evaluator = Evaluator(qa_data_path, corpus_data_path, project_dir)
175
+ evaluator.restart_trial(trial_path)
176
+
177
+
178
+ @click.command()
179
+ @click.option(
180
+ "--config",
181
+ "-c",
182
+ help="Path to config yaml file. Must be yaml or yml file.",
183
+ type=str,
184
+ )
185
+ @click.option(
186
+ "--qa_data_path", help="Path to QA dataset. Must be parquet file.", type=str
187
+ )
188
+ @click.option(
189
+ "--corpus_data_path", help="Path to corpus dataset. Must be parquet file.", type=str
190
+ )
191
+ def validate(config, qa_data_path, corpus_data_path):
192
+ if not config.endswith(".yaml") and not config.endswith(".yml"):
193
+ raise ValueError(f"Config file {config} is not a parquet file.")
194
+ if not os.path.exists(config):
195
+ raise ValueError(f"Config file {config} does not exist.")
196
+ validator = Validator(qa_data_path=qa_data_path, corpus_data_path=corpus_data_path)
197
+ validator.validate(config)
198
+
199
+
200
+ cli.add_command(evaluate, "evaluate")
201
+ cli.add_command(run_api, "run_api")
202
+ cli.add_command(run_web, "run_web")
203
+ cli.add_command(run_dashboard, "dashboard")
204
+ cli.add_command(extract_best_config, "extract_best_config")
205
+ cli.add_command(restart_evaluate, "restart_evaluate")
206
+ cli.add_command(validate, "validate")
207
+
208
+ if __name__ == "__main__":
209
+ cli()
autorag/dashboard.py ADDED
@@ -0,0 +1,199 @@
1
+ import ast
2
+ import logging
3
+ import os
4
+ from typing import Dict, List
5
+
6
+ import matplotlib.pyplot as plt
7
+ import pandas as pd
8
+ import panel as pn
9
+ import seaborn as sns
10
+ import yaml
11
+ from bokeh.models import NumberFormatter, BooleanFormatter
12
+
13
+ from autorag.utils.util import dict_to_markdown, dict_to_markdown_table
14
+
15
+ pn.extension(
16
+ "terminal",
17
+ "tabulator",
18
+ "mathjax",
19
+ "ipywidgets",
20
+ console_output="disable",
21
+ sizing_mode="stretch_width",
22
+ css_files=[
23
+ "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"
24
+ ],
25
+ )
26
+ logger = logging.getLogger("AutoRAG")
27
+
28
+
29
+ def find_node_dir(trial_dir: str) -> List[str]:
30
+ trial_summary_df = pd.read_csv(os.path.join(trial_dir, "summary.csv"))
31
+ result_paths = []
32
+ for idx, row in trial_summary_df.iterrows():
33
+ node_line_name = row["node_line_name"]
34
+ node_type = row["node_type"]
35
+ result_paths.append(os.path.join(trial_dir, node_line_name, node_type))
36
+ return result_paths
37
+
38
+
39
+ def get_metric_values(node_summary_df: pd.DataFrame) -> Dict:
40
+ non_metric_column_names = [
41
+ "filename",
42
+ "module_name",
43
+ "module_params",
44
+ "execution_time",
45
+ "average_output_token",
46
+ "is_best",
47
+ ]
48
+ best_row = node_summary_df.loc[node_summary_df["is_best"]].drop(
49
+ columns=non_metric_column_names, errors="ignore"
50
+ )
51
+ assert len(best_row) == 1, "The best module must be only one."
52
+ return best_row.iloc[0].to_dict()
53
+
54
+
55
+ def make_trial_summary_md(trial_dir):
56
+ markdown_text = f"""# Trial Result Summary
57
+ - Trial Directory : {trial_dir}
58
+
59
+ """
60
+ node_dirs = find_node_dir(trial_dir)
61
+ for node_dir in node_dirs:
62
+ node_summary_filepath = os.path.join(node_dir, "summary.csv")
63
+ node_type = os.path.basename(node_dir)
64
+ node_summary_df = pd.read_csv(node_summary_filepath)
65
+ best_row = node_summary_df.loc[node_summary_df["is_best"]].iloc[0]
66
+ metric_dict = get_metric_values(node_summary_df)
67
+ markdown_text += f"""---
68
+
69
+ ## {node_type} best module
70
+
71
+ ### Module Name
72
+
73
+ {best_row["module_name"]}
74
+
75
+ ### Module Params
76
+
77
+ {dict_to_markdown(ast.literal_eval(best_row["module_params"]), level=3)}
78
+
79
+ ### Metric Values
80
+
81
+ {dict_to_markdown_table(metric_dict, key_column_name="metric_name", value_column_name="metric_value")}
82
+
83
+ """
84
+
85
+ return markdown_text
86
+
87
+
88
+ def node_view(node_dir: str):
89
+ non_metric_column_names = [
90
+ "filename",
91
+ "module_name",
92
+ "module_params",
93
+ "execution_time",
94
+ "average_output_token",
95
+ "is_best",
96
+ ]
97
+ summary_df = pd.read_csv(os.path.join(node_dir, "summary.csv"))
98
+ bokeh_formatters = {
99
+ "float": NumberFormatter(format="0.000"),
100
+ "bool": BooleanFormatter(),
101
+ }
102
+ first_df = pd.read_parquet(os.path.join(node_dir, "0.parquet"), engine="pyarrow")
103
+
104
+ each_module_df_widget = pn.widgets.Tabulator(
105
+ pd.DataFrame(columns=first_df.columns),
106
+ name="Module DataFrame",
107
+ formatters=bokeh_formatters,
108
+ pagination="local",
109
+ page_size=20,
110
+ widths=150,
111
+ )
112
+
113
+ def change_module_widget(event):
114
+ if event.column == "detail":
115
+ filename = summary_df["filename"].iloc[event.row]
116
+ filepath = os.path.join(node_dir, filename)
117
+ each_module_df = pd.read_parquet(filepath, engine="pyarrow")
118
+ each_module_df_widget.value = each_module_df
119
+
120
+ df_widget = pn.widgets.Tabulator(
121
+ summary_df,
122
+ name="Summary DataFrame",
123
+ formatters=bokeh_formatters,
124
+ buttons={"detail": '<i class="fa fa-eye"></i>'},
125
+ widths=150,
126
+ )
127
+ df_widget.on_click(change_module_widget)
128
+
129
+ try:
130
+ fig, ax = plt.subplots(figsize=(10, 5))
131
+ metric_df = summary_df.drop(columns=non_metric_column_names, errors="ignore")
132
+ sns.stripplot(data=metric_df, ax=ax)
133
+ strip_plot_pane = pn.pane.Matplotlib(fig, tight=True)
134
+
135
+ fig2, ax2 = plt.subplots(figsize=(10, 5))
136
+ sns.boxplot(data=metric_df, ax=ax2)
137
+ box_plot_pane = pn.pane.Matplotlib(fig2, tight=True)
138
+ plot_pane = pn.Row(strip_plot_pane, box_plot_pane)
139
+
140
+ layout = pn.Column(
141
+ "## Summary distribution plot",
142
+ plot_pane,
143
+ "## Summary DataFrame",
144
+ df_widget,
145
+ "## Module Result DataFrame",
146
+ each_module_df_widget,
147
+ )
148
+ except Exception as e:
149
+ logger.error(f"Skipping make boxplot and stripplot with error {e}")
150
+ layout = pn.Column("## Summary DataFrame", df_widget)
151
+ layout.servable()
152
+ return layout
153
+
154
+
155
+ CSS = """
156
+ div.card-margin:nth-child(1) {
157
+ max-height: 300px;
158
+ }
159
+ div.card-margin:nth-child(2) {
160
+ max-height: 400px;
161
+ }
162
+ """
163
+
164
+
165
+ def yaml_to_markdown(yaml_filepath):
166
+ markdown_content = ""
167
+ with open(yaml_filepath, "r", encoding="utf-8") as file:
168
+ try:
169
+ content = yaml.safe_load(file)
170
+ markdown_content += f"## {os.path.basename(yaml_filepath)}\n```yaml\n{yaml.safe_dump(content, allow_unicode=True)}\n```\n\n"
171
+ except yaml.YAMLError as exc:
172
+ print(f"Error in {yaml_filepath}: {exc}")
173
+ return markdown_content
174
+
175
+
176
+ def run(trial_dir: str, port: int = 7690):
177
+ trial_summary_md = make_trial_summary_md(trial_dir=trial_dir)
178
+ trial_summary_tab = pn.pane.Markdown(trial_summary_md, sizing_mode="stretch_width")
179
+
180
+ node_views = [
181
+ (str(os.path.basename(node_dir)), node_view(node_dir))
182
+ for node_dir in find_node_dir(trial_dir)
183
+ ]
184
+
185
+ yaml_file_markdown = yaml_to_markdown(os.path.join(trial_dir, "config.yaml"))
186
+
187
+ yaml_file = pn.pane.Markdown(yaml_file_markdown, sizing_mode="stretch_width")
188
+
189
+ tabs = pn.Tabs(
190
+ ("Summary", trial_summary_tab),
191
+ *node_views,
192
+ ("Used YAML file", yaml_file),
193
+ dynamic=True,
194
+ )
195
+
196
+ template = pn.template.FastListTemplate(
197
+ site="AutoRAG", title="Dashboard", main=[tabs], raw_css=[CSS]
198
+ ).servable()
199
+ template.show(port=port)
@@ -0,0 +1,109 @@
1
+ import logging
2
+ from typing import List, Callable
3
+
4
+ from langchain_community.document_loaders import (
5
+ PDFMinerLoader,
6
+ PDFPlumberLoader,
7
+ PyPDFium2Loader,
8
+ PyPDFLoader,
9
+ PyMuPDFLoader,
10
+ UnstructuredPDFLoader,
11
+ CSVLoader,
12
+ JSONLoader,
13
+ UnstructuredMarkdownLoader,
14
+ BSHTMLLoader,
15
+ UnstructuredXMLLoader,
16
+ DirectoryLoader,
17
+ )
18
+ from langchain_unstructured import UnstructuredLoader
19
+ from langchain_upstage import UpstageLayoutAnalysisLoader
20
+
21
+ from llama_index.core.node_parser import (
22
+ TokenTextSplitter,
23
+ SentenceSplitter,
24
+ SentenceWindowNodeParser,
25
+ SemanticSplitterNodeParser,
26
+ SemanticDoubleMergingSplitterNodeParser,
27
+ SimpleFileNodeParser,
28
+ )
29
+ from langchain.text_splitter import (
30
+ RecursiveCharacterTextSplitter,
31
+ CharacterTextSplitter,
32
+ KonlpyTextSplitter,
33
+ SentenceTransformersTokenTextSplitter,
34
+ )
35
+
36
+ from autorag import LazyInit
37
+
38
+ logger = logging.getLogger("AutoRAG")
39
+
40
+ parse_modules = {
41
+ # PDF
42
+ "pdfminer": PDFMinerLoader,
43
+ "pdfplumber": PDFPlumberLoader,
44
+ "pypdfium2": PyPDFium2Loader,
45
+ "pypdf": PyPDFLoader,
46
+ "pymupdf": PyMuPDFLoader,
47
+ "unstructuredpdf": UnstructuredPDFLoader,
48
+ # Common File Types
49
+ # 1. CSV
50
+ "csv": CSVLoader,
51
+ # 2. JSON
52
+ "json": JSONLoader,
53
+ # 3. Markdown
54
+ "unstructuredmarkdown": UnstructuredMarkdownLoader,
55
+ # 4. HTML
56
+ "bshtml": BSHTMLLoader,
57
+ # 5. XML
58
+ "unstructuredxml": UnstructuredXMLLoader,
59
+ # 6. All files
60
+ "directory": DirectoryLoader,
61
+ "unstructured": UnstructuredLoader,
62
+ "upstagedocumentparse": UpstageLayoutAnalysisLoader,
63
+ }
64
+
65
+ chunk_modules = {
66
+ # Llama Index
67
+ # Token
68
+ "token": TokenTextSplitter,
69
+ # Sentence
70
+ "sentence": SentenceSplitter,
71
+ # window
72
+ "sentencewindow": SentenceWindowNodeParser,
73
+ # Semantic
74
+ "semantic_llama_index": SemanticSplitterNodeParser,
75
+ "semanticdoublemerging": SemanticDoubleMergingSplitterNodeParser,
76
+ # Simple
77
+ "simplefile": SimpleFileNodeParser,
78
+ # LangChain
79
+ # Token
80
+ "sentencetransformerstoken": SentenceTransformersTokenTextSplitter,
81
+ # Character
82
+ "recursivecharacter": RecursiveCharacterTextSplitter,
83
+ "character": CharacterTextSplitter,
84
+ # Sentence
85
+ "konlpy": KonlpyTextSplitter,
86
+ }
87
+
88
+
89
+ def split_by_sentence_kiwi() -> Callable[[str], List[str]]:
90
+ try:
91
+ from kiwipiepy import Kiwi
92
+ except ImportError:
93
+ raise ImportError(
94
+ "You need to install kiwipiepy to use 'ko_kiwi' tokenizer. "
95
+ "Please install kiwipiepy by running 'pip install kiwipiepy'. "
96
+ "Or install Korean version of AutoRAG by running 'pip install AutoRAG[ko]'."
97
+ )
98
+ kiwi = Kiwi()
99
+
100
+ def split(text: str) -> List[str]:
101
+ kiwi_result = kiwi.split_into_sents(text)
102
+ sentences = list(map(lambda x: x.text, kiwi_result))
103
+
104
+ return sentences
105
+
106
+ return split
107
+
108
+
109
+ sentence_splitter_modules = {"kiwi": LazyInit(split_by_sentence_kiwi)}
@@ -0,0 +1,2 @@
1
+ from .llama_index_chunk import llama_index_chunk
2
+ from .langchain_chunk import langchain_chunk