right-rag 0.1.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.
- right_rag/__init__.py +2 -0
- right_rag/__main__.py +6 -0
- right_rag/cli.py +130 -0
- right_rag/interfaces.py +155 -0
- right_rag/model.py +481 -0
- right_rag/pipeline.py +1011 -0
- right_rag/pipeline_state.py +154 -0
- right_rag/registry.py +455 -0
- right_rag/spec/__init__.py +1 -0
- right_rag/spec/answer_generators/__init__.py +1 -0
- right_rag/spec/answer_generators/default.py +21 -0
- right_rag/spec/chunk_parsers/__init__.py +1 -0
- right_rag/spec/chunk_parsers/default.py +47 -0
- right_rag/spec/chunk_savers/__init__.py +1 -0
- right_rag/spec/chunk_savers/default.py +88 -0
- right_rag/spec/document_loaders/__init__.py +1 -0
- right_rag/spec/document_loaders/default.py +97 -0
- right_rag/spec/evaluators/__init__.py +1 -0
- right_rag/spec/evaluators/default.py +119 -0
- right_rag/spec/reporters/__init__.py +1 -0
- right_rag/spec/reporters/default.py +100 -0
- right_rag/spec/retrievers/__init__.py +1 -0
- right_rag/spec/retrievers/default.py +55 -0
- right_rag/spec/unit_parsers/__init__.py +1 -0
- right_rag/spec/unit_parsers/default.py +50 -0
- right_rag/spec/unit_savers/__init__.py +1 -0
- right_rag/spec/unit_savers/default.py +93 -0
- right_rag-0.1.0.dist-info/METADATA +552 -0
- right_rag-0.1.0.dist-info/RECORD +32 -0
- right_rag-0.1.0.dist-info/WHEEL +4 -0
- right_rag-0.1.0.dist-info/entry_points.txt +2 -0
- right_rag-0.1.0.dist-info/licenses/LICENSE +21 -0
right_rag/__init__.py
ADDED
right_rag/__main__.py
ADDED
right_rag/cli.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .pipeline import cli as pipeline_cli
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
SPEC_PACKAGES = [
|
|
11
|
+
"document_loaders",
|
|
12
|
+
"unit_parsers",
|
|
13
|
+
"chunk_parsers",
|
|
14
|
+
"unit_savers",
|
|
15
|
+
"chunk_savers",
|
|
16
|
+
"retrievers",
|
|
17
|
+
"answer_generators",
|
|
18
|
+
"evaluators",
|
|
19
|
+
"reporters",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main(argv: list[str] | None = None) -> None:
|
|
24
|
+
sys.path.insert(0, str(Path.cwd()))
|
|
25
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
|
26
|
+
|
|
27
|
+
if not args or args[0] in {"-h", "--help"}:
|
|
28
|
+
print_help()
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
if args[0] == "init":
|
|
32
|
+
init_cli(args[1:])
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
pipeline_cli(args)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def print_help() -> None:
|
|
39
|
+
print(
|
|
40
|
+
"usage: right-rag {init,index,query,evaluate} ...\n\n"
|
|
41
|
+
"commands:\n"
|
|
42
|
+
" init [path] create a right-rag project\n"
|
|
43
|
+
" index build unit and chunk artifacts\n"
|
|
44
|
+
" query TEXT query saved artifacts\n"
|
|
45
|
+
" evaluate [cases] evaluate RAG quality\n"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def init_cli(argv: list[str]) -> None:
|
|
50
|
+
parser = argparse.ArgumentParser(prog="right-rag init")
|
|
51
|
+
parser.add_argument("path", nargs="?", default=".")
|
|
52
|
+
parser.add_argument("--force", action="store_true")
|
|
53
|
+
args = parser.parse_args(argv)
|
|
54
|
+
|
|
55
|
+
init_project(Path(args.path), force=args.force)
|
|
56
|
+
print(f"Created right-rag project at {Path(args.path).resolve()}")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def init_project(root: Path, force: bool = False) -> None:
|
|
60
|
+
root = root.resolve()
|
|
61
|
+
if root.exists() and any(root.iterdir()) and not force:
|
|
62
|
+
raise SystemExit(f"{root} is not empty. Use --force to write into it.")
|
|
63
|
+
|
|
64
|
+
for path in [
|
|
65
|
+
root / "data" / "documents",
|
|
66
|
+
root / "output",
|
|
67
|
+
root / "eval",
|
|
68
|
+
root / "spec",
|
|
69
|
+
]:
|
|
70
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
|
|
72
|
+
write_once(root / "right_rag.toml", RIGHT_RAG_TOML, force)
|
|
73
|
+
write_once(root / ".gitignore", GITIGNORE, force)
|
|
74
|
+
write_once(root / "data" / "documents" / ".gitkeep", "", force)
|
|
75
|
+
write_once(root / "eval" / "cases.example.json", EVAL_CASES, force)
|
|
76
|
+
write_once(root / "spec" / "__init__.py", "", force)
|
|
77
|
+
|
|
78
|
+
for package in SPEC_PACKAGES:
|
|
79
|
+
directory = root / "spec" / package
|
|
80
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
81
|
+
write_once(directory / "__init__.py", "", force)
|
|
82
|
+
write_once(directory / "default.py", default_reexport(package), force)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def write_once(path: Path, text: str, force: bool) -> None:
|
|
86
|
+
if path.exists() and not force:
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
path.write_text(text, encoding="utf-8")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def default_reexport(package: str) -> str:
|
|
93
|
+
return f"from right_rag.spec.{package}.default import * # noqa: F401,F403\n"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
RIGHT_RAG_TOML = """\
|
|
97
|
+
[right-rag]
|
|
98
|
+
spec_root_package = "spec"
|
|
99
|
+
document_root_folder = "data/documents"
|
|
100
|
+
state_path = ".right-rag-state.json"
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
GITIGNORE = """\
|
|
105
|
+
__pycache__/
|
|
106
|
+
*.py[oc]
|
|
107
|
+
.right-rag-state.json
|
|
108
|
+
output/
|
|
109
|
+
data/documents/*
|
|
110
|
+
!data/documents/.gitkeep
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
EVAL_CASES = """\
|
|
115
|
+
{
|
|
116
|
+
"cases": [
|
|
117
|
+
{
|
|
118
|
+
"id": "edit-me",
|
|
119
|
+
"query": "replace this with a real domain question",
|
|
120
|
+
"expected": {
|
|
121
|
+
"answer_contains": [],
|
|
122
|
+
"hit_contains": [],
|
|
123
|
+
"hit_ids": [],
|
|
124
|
+
"must_not_contain": []
|
|
125
|
+
},
|
|
126
|
+
"tags": ["smoke"]
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
"""
|
right_rag/interfaces.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from collections.abc import Iterable, Iterator, Mapping
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .model import Answer, Chunk, Document, EvalCase, EvalResult, Hit, ParsedUnit, Query, Unit
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DocumentLoader(ABC):
|
|
11
|
+
@property
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def loader_name(self) -> str:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def __iter__(self) -> Iterator[Document]:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
def load_documents(self, root_folder: str) -> Iterator[Document]:
|
|
21
|
+
return iter(self)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class UnitParser(ABC):
|
|
25
|
+
@property
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def parser_name(self) -> str:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def parser_can_handle_document(self, document: Document) -> bool:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
def parse_document(self, document: Document) -> Iterable[ParsedUnit]:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ChunkParser(ABC):
|
|
40
|
+
@property
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def parser_name(self) -> str:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def parse_unit(self, unit: Unit) -> Iterable[Chunk]:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class UnitArtifact(ABC):
|
|
51
|
+
@property
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def name(self) -> str:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def retrieve(self, query: Query) -> Iterable[Hit]:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ChunkArtifact(ABC):
|
|
62
|
+
@property
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def name(self) -> str:
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def retrieve(self, query: Query) -> Iterable[Hit]:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ChunkSaver(ABC):
|
|
73
|
+
@property
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def saver_name(self) -> str:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
@abstractmethod
|
|
79
|
+
def save_chunks(self, chunks: Iterable[Chunk]) -> ChunkArtifact:
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
def open_chunk_artifact(self) -> ChunkArtifact:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class UnitSaver(ABC):
|
|
88
|
+
@property
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def saver_name(self) -> str:
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def save_units(self, units: Iterable[Unit]) -> UnitArtifact:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
def open_unit_artifact(self) -> UnitArtifact:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class Retriever(ABC):
|
|
103
|
+
@property
|
|
104
|
+
@abstractmethod
|
|
105
|
+
def retriever_name(self) -> str:
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
def query_options(self) -> Mapping[str, str]:
|
|
109
|
+
return {}
|
|
110
|
+
|
|
111
|
+
@abstractmethod
|
|
112
|
+
def retrieve(
|
|
113
|
+
self,
|
|
114
|
+
query: Query,
|
|
115
|
+
unit_artifacts: Mapping[str, UnitArtifact],
|
|
116
|
+
chunk_artifacts: Mapping[str, ChunkArtifact],
|
|
117
|
+
) -> list[Hit]:
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class AnswerGenerator(ABC):
|
|
122
|
+
@property
|
|
123
|
+
@abstractmethod
|
|
124
|
+
def generator_name(self) -> str:
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
@abstractmethod
|
|
128
|
+
def generate(self, query: Query, hits: Iterable[Hit]) -> Answer:
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class Evaluator(ABC):
|
|
133
|
+
@property
|
|
134
|
+
@abstractmethod
|
|
135
|
+
def evaluator_name(self) -> str:
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
@abstractmethod
|
|
139
|
+
def evaluate(self, case: EvalCase, answer: Answer) -> EvalResult:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class Reporter(ABC):
|
|
144
|
+
@property
|
|
145
|
+
@abstractmethod
|
|
146
|
+
def reporter_name(self) -> str:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
@abstractmethod
|
|
150
|
+
def write_report(
|
|
151
|
+
self,
|
|
152
|
+
results: Iterable[EvalResult],
|
|
153
|
+
output_path: str | Path,
|
|
154
|
+
) -> None:
|
|
155
|
+
pass
|