rag-sentinel 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
rag_sentinel/__init__.py CHANGED
@@ -1,5 +1,23 @@
1
1
  """
2
- RAGSentinel - RAG Evaluation Framework
2
+ RAGSentinel - RAG Evaluation Framework using Ragas and MLflow.
3
+
4
+ A pip-installable package for evaluating Retrieval-Augmented Generation (RAG)
5
+ applications using Ragas metrics with MLflow tracking.
6
+
7
+ Features:
8
+ - Evaluate RAG pipelines with Faithfulness, AnswerRelevancy,
9
+ ContextPrecision, and AnswerCorrectness metrics
10
+ - Support for Azure OpenAI, OpenAI, and Ollama LLM providers
11
+ - Automatic MLflow server management and result tracking
12
+ - Flexible authentication (Cookie, Bearer, Custom Header)
13
+
14
+ Usage:
15
+ $ pip install rag-sentinel
16
+ $ rag-sentinel init
17
+ $ rag-sentinel run
18
+
19
+ Author: RAGSentinel Team
20
+ License: MIT
3
21
  """
4
22
 
5
23
  __version__ = "0.1.0"
rag_sentinel/cli.py CHANGED
@@ -1,8 +1,19 @@
1
1
  """
2
- RAGSentinel CLI - Command Line Interface
2
+ RAGSentinel CLI - Command Line Interface.
3
+
4
+ This module provides the command-line interface for RAGSentinel.
5
+
6
+ Commands:
7
+ init - Initialize a new project with configuration templates
8
+ run - Run RAG evaluation (auto-starts MLflow server)
9
+ validate - Validate configuration files
10
+
11
+ Example:
12
+ $ rag-sentinel init
13
+ $ rag-sentinel run
14
+ $ rag-sentinel validate
3
15
  """
4
16
 
5
- import os
6
17
  import sys
7
18
  import shutil
8
19
  import socket
@@ -11,58 +22,103 @@ import time
11
22
  import argparse
12
23
  from pathlib import Path
13
24
 
14
- # Get the templates directory path
25
+
26
+ # =============================================================================
27
+ # Constants
28
+ # =============================================================================
29
+
15
30
  TEMPLATES_DIR = Path(__file__).parent / "templates"
16
31
 
17
32
 
33
+ # =============================================================================
34
+ # Helper Functions
35
+ # =============================================================================
36
+
18
37
  def is_port_in_use(host, port):
19
- """Check if a port is in use."""
38
+ """
39
+ Check if a port is already in use.
40
+
41
+ Args:
42
+ host: The hostname to check (e.g., "127.0.0.1")
43
+ port: The port number to check
44
+
45
+ Returns:
46
+ bool: True if port is in use, False otherwise
47
+ """
20
48
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
21
49
  return s.connect_ex((host, port)) == 0
22
50
 
23
51
 
24
52
  def start_mlflow_server(host="127.0.0.1", port=5001):
25
- """Start MLflow server as a background process."""
53
+ """
54
+ Start MLflow tracking server as a background process.
55
+
56
+ If the server is already running on the specified port, this function
57
+ will skip starting a new instance.
58
+
59
+ Args:
60
+ host: The hostname to bind the server to (default: "127.0.0.1")
61
+ port: The port number for the server (default: 5001)
62
+
63
+ Returns:
64
+ subprocess.Popen or None: The server process, or None if already running
65
+ """
26
66
  if is_port_in_use(host, port):
27
67
  print(f"✓ MLflow server already running at http://{host}:{port}")
28
68
  return None
29
-
69
+
30
70
  print(f"🚀 Starting MLflow server at http://{host}:{port}...")
31
-
71
+
72
+ # Start MLflow server as a detached background process
32
73
  process = subprocess.Popen(
33
74
  [sys.executable, "-m", "mlflow", "server", "--host", host, "--port", str(port)],
34
75
  stdout=subprocess.DEVNULL,
35
76
  stderr=subprocess.DEVNULL,
36
77
  creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == "win32" else 0
37
78
  )
38
-
39
- # Wait for server to start
79
+
80
+ # Wait for server to become available (max 10 seconds)
40
81
  for _ in range(10):
41
82
  time.sleep(1)
42
83
  if is_port_in_use(host, port):
43
84
  print(f"✓ MLflow server started at http://{host}:{port}")
44
85
  return process
45
-
86
+
46
87
  print("⚠ MLflow server may not have started properly")
47
88
  return process
48
89
 
49
90
 
91
+ # =============================================================================
92
+ # CLI Commands
93
+ # =============================================================================
94
+
50
95
  def cmd_init(args):
51
- """Initialize project with config templates."""
96
+ """
97
+ Initialize a new project with configuration templates.
98
+
99
+ Copies the following template files to the current directory:
100
+ - .env : LLM and Embeddings API configuration
101
+ - config.ini : Application settings (URLs, auth, dataset path)
102
+ - rag_eval_config.yaml : Master configuration with placeholders
103
+
104
+ Args:
105
+ args: Parsed command-line arguments (includes --force flag)
106
+ """
52
107
  print("=" * 50)
53
108
  print("RAGSentinel - Project Initialization")
54
109
  print("=" * 50)
55
-
110
+
111
+ # Template files to copy: (source_name, destination_name)
56
112
  files_to_copy = [
57
113
  (".env.template", ".env"),
58
114
  ("config.ini.template", "config.ini"),
59
115
  ("rag_eval_config.yaml", "rag_eval_config.yaml"),
60
116
  ]
61
-
117
+
62
118
  for src_name, dest_name in files_to_copy:
63
119
  src_path = TEMPLATES_DIR / src_name
64
120
  dest_path = Path.cwd() / dest_name
65
-
121
+
66
122
  if dest_path.exists() and not args.force:
67
123
  print(f"⚠ {dest_name} already exists (use --force to overwrite)")
68
124
  else:
@@ -71,7 +127,8 @@ def cmd_init(args):
71
127
  print(f"✓ Created {dest_name}")
72
128
  else:
73
129
  print(f"✗ Template not found: {src_name}")
74
-
130
+
131
+ # Display next steps for the user
75
132
  print("\n" + "=" * 50)
76
133
  print("Next steps:")
77
134
  print(" 1. Edit .env with your LLM/Embeddings API keys")
@@ -82,49 +139,71 @@ def cmd_init(args):
82
139
 
83
140
 
84
141
  def cmd_run(args):
85
- """Run the RAG evaluation."""
86
- # Check if config files exist
142
+ """
143
+ Run the RAG evaluation pipeline.
144
+
145
+ This command:
146
+ 1. Validates that all required config files exist
147
+ 2. Starts MLflow server (unless --no-server is specified)
148
+ 3. Runs the evaluation using the evaluator module
149
+
150
+ Args:
151
+ args: Parsed command-line arguments (includes --no-server flag)
152
+ """
153
+ # Check if all required configuration files exist
87
154
  required_files = [".env", "config.ini", "rag_eval_config.yaml"]
88
155
  missing_files = [f for f in required_files if not Path(f).exists()]
89
-
156
+
90
157
  if missing_files:
91
158
  print("❌ Missing configuration files:")
92
159
  for f in missing_files:
93
160
  print(f" - {f}")
94
161
  print("\nRun 'rag-sentinel init' first to create config files.")
95
162
  sys.exit(1)
96
-
97
- # Start MLflow server if not running
163
+
164
+ # Start MLflow server if not disabled
98
165
  if not args.no_server:
99
166
  start_mlflow_server()
100
-
101
- # Import and run evaluation
167
+
168
+ # Import and run the evaluation
102
169
  from rag_sentinel.evaluator import run_evaluation
103
170
  run_evaluation()
104
171
 
105
172
 
106
173
  def cmd_validate(args):
107
- """Validate configuration files."""
174
+ """
175
+ Validate configuration files without running evaluation.
176
+
177
+ Checks:
178
+ - All required files exist (.env, config.ini, rag_eval_config.yaml)
179
+ - Configuration can be loaded and parsed correctly
180
+ - Displays key configuration values for verification
181
+
182
+ Args:
183
+ args: Parsed command-line arguments (unused, kept for consistency)
184
+ """
185
+ _ = args # Unused, but required for command handler signature
186
+
108
187
  print("=" * 50)
109
188
  print("RAGSentinel - Configuration Validation")
110
189
  print("=" * 50)
111
-
112
- # Check files exist
190
+
191
+ # Check if all required files exist
113
192
  files_to_check = [".env", "config.ini", "rag_eval_config.yaml"]
114
193
  all_exist = True
115
-
194
+
116
195
  for f in files_to_check:
117
196
  if Path(f).exists():
118
197
  print(f"✓ {f} exists")
119
198
  else:
120
199
  print(f"✗ {f} missing")
121
200
  all_exist = False
122
-
201
+
123
202
  if not all_exist:
124
203
  print("\n❌ Some files are missing. Run 'rag-sentinel init' first.")
125
204
  return
126
-
127
- # Try to load config
205
+
206
+ # Attempt to load and validate configuration
128
207
  try:
129
208
  from rag_sentinel.evaluator import load_config
130
209
  config = load_config()
@@ -137,28 +216,38 @@ def cmd_validate(args):
137
216
  print(f"\n❌ Configuration error: {e}")
138
217
 
139
218
 
219
+ # =============================================================================
220
+ # Main Entry Point
221
+ # =============================================================================
222
+
140
223
  def main():
141
- """Main entry point for CLI."""
224
+ """
225
+ Main entry point for the RAGSentinel CLI.
226
+
227
+ Parses command-line arguments and dispatches to the appropriate
228
+ command handler (init, run, or validate).
229
+ """
142
230
  parser = argparse.ArgumentParser(
143
231
  prog="rag-sentinel",
144
232
  description="RAGSentinel - RAG Evaluation Framework using Ragas and MLflow"
145
233
  )
146
-
234
+
147
235
  subparsers = parser.add_subparsers(dest="command", help="Available commands")
148
-
149
- # init command
236
+
237
+ # 'init' command - Initialize project with config templates
150
238
  init_parser = subparsers.add_parser("init", help="Initialize project with config templates")
151
239
  init_parser.add_argument("--force", "-f", action="store_true", help="Overwrite existing files")
152
-
153
- # run command
240
+
241
+ # 'run' command - Run RAG evaluation
154
242
  run_parser = subparsers.add_parser("run", help="Run RAG evaluation")
155
243
  run_parser.add_argument("--no-server", action="store_true", help="Don't start MLflow server")
156
-
157
- # validate command
158
- validate_parser = subparsers.add_parser("validate", help="Validate configuration files")
159
-
244
+
245
+ # 'validate' command - Validate configuration files
246
+ subparsers.add_parser("validate", help="Validate configuration files")
247
+
160
248
  args = parser.parse_args()
161
-
249
+
250
+ # Dispatch to appropriate command handler
162
251
  if args.command == "init":
163
252
  cmd_init(args)
164
253
  elif args.command == "run":
rag_sentinel/evaluator.py CHANGED
@@ -1,5 +1,9 @@
1
1
  """
2
- RAGSentinel Evaluator - Core evaluation logic
2
+ RAGSentinel Evaluator - Core evaluation logic.
3
+
4
+ This module contains the main evaluation pipeline for RAGSentinel.
5
+ It handles configuration loading, LLM initialization, API communication,
6
+ Ragas metrics evaluation, and MLflow result logging.
3
7
  """
4
8
 
5
9
  import os
@@ -15,19 +19,34 @@ from ragas import evaluate, RunConfig
15
19
  from ragas.metrics import faithfulness, answer_relevancy, context_precision, answer_correctness
16
20
 
17
21
 
22
+ # =============================================================================
23
+ # Configuration Loading
24
+ # =============================================================================
25
+
26
+
18
27
  def resolve_placeholder(value, env_vars, ini_config):
19
- """Resolve ${ENV:...} and ${INI:...} placeholders."""
28
+ """
29
+ Resolve ${ENV:...} and ${INI:...} placeholders in a string value.
30
+
31
+ Args:
32
+ value: String that may contain placeholders
33
+ env_vars: Dictionary of environment variables
34
+ ini_config: ConfigParser object with ini file contents
35
+
36
+ Returns:
37
+ str: Value with all placeholders resolved
38
+ """
20
39
  if not isinstance(value, str):
21
40
  return value
22
41
 
23
- # Resolve ${ENV:VAR_NAME}
42
+ # Resolve ${ENV:VAR_NAME} - reads from environment variables
24
43
  env_pattern = r'\$\{ENV:([^}]+)\}'
25
44
  def env_replacer(match):
26
45
  var_name = match.group(1)
27
46
  return env_vars.get(var_name, '')
28
47
  value = re.sub(env_pattern, env_replacer, value)
29
48
 
30
- # Resolve ${INI:section.key}
49
+ # Resolve ${INI:section.key} - reads from config.ini
31
50
  ini_pattern = r'\$\{INI:([^}]+)\}'
32
51
  def ini_replacer(match):
33
52
  path = match.group(1)
@@ -43,7 +62,17 @@ def resolve_placeholder(value, env_vars, ini_config):
43
62
 
44
63
 
45
64
  def resolve_config(obj, env_vars, ini_config):
46
- """Recursively resolve all placeholders in config."""
65
+ """
66
+ Recursively resolve all placeholders in a configuration object.
67
+
68
+ Args:
69
+ obj: Configuration object (dict, list, or str)
70
+ env_vars: Dictionary of environment variables
71
+ ini_config: ConfigParser object
72
+
73
+ Returns:
74
+ Configuration object with all placeholders resolved
75
+ """
47
76
  if isinstance(obj, dict):
48
77
  return {k: resolve_config(v, env_vars, ini_config) for k, v in obj.items()}
49
78
  elif isinstance(obj, list):
@@ -54,13 +83,21 @@ def resolve_config(obj, env_vars, ini_config):
54
83
 
55
84
 
56
85
  def load_config():
57
- """Load configuration from .env, config.ini, and rag_eval_config.yaml."""
86
+ """
87
+ Load and merge configuration from .env, config.ini, and rag_eval_config.yaml.
88
+
89
+ Returns:
90
+ dict: Fully resolved configuration dictionary
91
+ """
92
+ # Load environment variables from .env file
58
93
  load_dotenv('.env')
59
94
  env_vars = dict(os.environ)
60
95
 
96
+ # Load INI configuration
61
97
  ini_config = configparser.ConfigParser()
62
98
  ini_config.read('config.ini')
63
99
 
100
+ # Load YAML configuration and resolve all placeholders
64
101
  with open('rag_eval_config.yaml', 'r') as f:
65
102
  yaml_config = yaml.safe_load(f)
66
103
 
@@ -2,51 +2,48 @@
2
2
  # RAGSentinel Environment Variables
3
3
  # =============================================================================
4
4
  # This file contains sensitive LLM and Embeddings configuration.
5
- # Copy this file to .env and fill in your values.
6
- # DO NOT commit .env to version control!
7
- # =============================================================================
8
5
 
9
- # =============================================================================
6
+
10
7
  # LLM Configuration
11
- # =============================================================================
8
+
12
9
  # Provider options: azure, openai, ollama
13
- LLM_PROVIDER=azure
10
+ LLM_PROVIDER=
14
11
 
15
12
  # Azure OpenAI LLM Configuration
16
13
  AZURE_LLM_API_KEY=
17
14
  AZURE_LLM_ENDPOINT=
18
15
  AZURE_LLM_DEPLOYMENT_NAME=
19
- AZURE_LLM_MODEL=gpt-4
16
+ AZURE_LLM_MODEL=
20
17
  AZURE_LLM_TEMPERATURE=0.0
21
- AZURE_LLM_API_VERSION=2024-02-15-preview
18
+ AZURE_LLM_API_VERSION=
22
19
 
23
20
  # OpenAI LLM Configuration
24
21
  OPENAI_LLM_API_KEY=
25
- OPENAI_LLM_MODEL=gpt-4
22
+ OPENAI_LLM_MODEL=
26
23
  OPENAI_LLM_TEMPERATURE=0.0
27
24
 
28
25
  # Ollama LLM Configuration
29
- OLLAMA_LLM_BASE_URL=http://localhost:11434
30
- OLLAMA_LLM_MODEL=llama3
26
+ OLLAMA_LLM_BASE_URL=
27
+ OLLAMA_LLM_MODEL=
31
28
  OLLAMA_LLM_TEMPERATURE=0.0
32
29
 
33
- # =============================================================================
30
+
34
31
  # Embeddings Configuration
35
- # =============================================================================
32
+
36
33
  # Provider options: azure, openai, ollama
37
- EMBEDDINGS_PROVIDER=azure
34
+ EMBEDDINGS_PROVIDER=
38
35
 
39
36
  # Azure OpenAI Embeddings Configuration
40
37
  AZURE_EMBEDDINGS_API_KEY=
41
38
  AZURE_EMBEDDINGS_ENDPOINT=
42
39
  AZURE_EMBEDDINGS_DEPLOYMENT_NAME=
43
- AZURE_EMBEDDINGS_API_VERSION=2024-02-15-preview
40
+ AZURE_EMBEDDINGS_API_VERSION=
44
41
 
45
42
  # OpenAI Embeddings Configuration
46
43
  OPENAI_EMBEDDINGS_API_KEY=
47
- OPENAI_EMBEDDINGS_MODEL=text-embedding-3-small
44
+ OPENAI_EMBEDDINGS_MODEL=
48
45
 
49
46
  # Ollama Embeddings Configuration
50
- OLLAMA_EMBEDDINGS_BASE_URL=http://localhost:11434
51
- OLLAMA_EMBEDDINGS_MODEL=nomic-embed-text
47
+ OLLAMA_EMBEDDINGS_BASE_URL=
48
+ OLLAMA_EMBEDDINGS_MODEL=
52
49
 
@@ -1,9 +1,5 @@
1
- # =============================================================================
2
1
  # RAGSentinel Configuration File
3
- # =============================================================================
4
- # This file contains non-sensitive configuration settings.
5
- # Copy this file to config.ini and fill in your values.
6
- # =============================================================================
2
+
7
3
 
8
4
  [mlflow]
9
5
  # MLflow tracking server URI
@@ -15,8 +11,8 @@ app_url =
15
11
 
16
12
  [endpoints]
17
13
  # API endpoint paths for context retrieval and answer generation
18
- context_path = /api/retrieve_context
19
- answer_path = /api/respond
14
+ context_path =
15
+ answer_path =
20
16
 
21
17
  [dataset]
22
18
  # Path to test dataset CSV file (columns: query, ground_truth, chat_id)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rag-sentinel
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: RAG Evaluation Framework using Ragas metrics and MLflow tracking
5
5
  Author: RAGSentinel Team
6
6
  License: MIT
@@ -0,0 +1,12 @@
1
+ rag_sentinel/__init__.py,sha256=jrVnGWJh60e1fucEP8DuQtFaXQcls8Rsw9tuUfKI87Y,672
2
+ rag_sentinel/cli.py,sha256=EacFBn_7TBsP1CPw9MmNUyGcAG-MO7c1UDz2v3wPkz4,8499
3
+ rag_sentinel/evaluator.py,sha256=tE_pqwaauxsZ3QU2yq-PxW6Ig1bqz31SAnydDcwAJfQ,12915
4
+ rag_sentinel/templates/.env.template,sha256=FabB1i4pUkU8gdNLRt2D8mgltY4AudClq8rx6vS33xc,1120
5
+ rag_sentinel/templates/config.ini.template,sha256=OeW21j4LXxXnFCPVvOZhdZOq1id0BQLgwS5ruXSrXBQ,1016
6
+ rag_sentinel/templates/rag_eval_config.yaml,sha256=zRPMOngALsbhgQbkKeNvXc8VVxzDJrASpSIpGTpVKlk,3080
7
+ rag_sentinel-0.1.1.dist-info/licenses/LICENSE,sha256=0bRNV4OZXZGaeA4PLR0CZKk1peLyIw977fV9K5jAGws,1074
8
+ rag_sentinel-0.1.1.dist-info/METADATA,sha256=j3sueNd2mWD350G05M_hYWgyHq8CJWj9aeVR9K7r7uM,2716
9
+ rag_sentinel-0.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
10
+ rag_sentinel-0.1.1.dist-info/entry_points.txt,sha256=ZBQp5JLnfMLjgLX3UdzX2rainIgvqkq36Wtf_VLa9ak,55
11
+ rag_sentinel-0.1.1.dist-info/top_level.txt,sha256=qk1BCc3wrLshA3-jf0Jb4bFlZF-ARIjZfYaqAW1Aq0E,13
12
+ rag_sentinel-0.1.1.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- rag_sentinel/__init__.py,sha256=kBMRLysR1HgWEkDM6Wlx908YUbmhDgqRJzM4AaHgQ9I,71
2
- rag_sentinel/cli.py,sha256=OtUHHOfAkspnMB_YYhs8YldzUhJv3Tqt79nm0A-bv5s,5415
3
- rag_sentinel/evaluator.py,sha256=QDr16pfIm1KEiJAQDMsuU4irGStQUmDC8WZTyVJcUsU,11719
4
- rag_sentinel/templates/.env.template,sha256=4gR-dIqErDMVvgvZe-frU3dvd1weeyQ_x0JUzaqvwCY,1751
5
- rag_sentinel/templates/config.ini.template,sha256=wVteTE_h1PHvs9I3hv37fSkiJ66fTHg9qOD_CCbYGXE,1403
6
- rag_sentinel/templates/rag_eval_config.yaml,sha256=zRPMOngALsbhgQbkKeNvXc8VVxzDJrASpSIpGTpVKlk,3080
7
- rag_sentinel-0.1.0.dist-info/licenses/LICENSE,sha256=0bRNV4OZXZGaeA4PLR0CZKk1peLyIw977fV9K5jAGws,1074
8
- rag_sentinel-0.1.0.dist-info/METADATA,sha256=xCc6G6RbJJR-Axom_ymWgiL2tpwh6-W9DqJydkvJ-ng,2716
9
- rag_sentinel-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
10
- rag_sentinel-0.1.0.dist-info/entry_points.txt,sha256=ZBQp5JLnfMLjgLX3UdzX2rainIgvqkq36Wtf_VLa9ak,55
11
- rag_sentinel-0.1.0.dist-info/top_level.txt,sha256=qk1BCc3wrLshA3-jf0Jb4bFlZF-ARIjZfYaqAW1Aq0E,13
12
- rag_sentinel-0.1.0.dist-info/RECORD,,