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
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)
|
autorag/data/__init__.py
ADDED
|
@@ -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)}
|