ragaai-catalyst 1.0.7b4__py3-none-any.whl → 1.0.8b2__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.
@@ -4,6 +4,7 @@ from .utils import response_checker
4
4
  from typing import Union
5
5
  import logging
6
6
  from .ragaai_catalyst import RagaAICatalyst
7
+ import pandas as pd
7
8
 
8
9
  logger = logging.getLogger(__name__)
9
10
  get_token = RagaAICatalyst.get_token
@@ -11,7 +12,7 @@ get_token = RagaAICatalyst.get_token
11
12
 
12
13
  class Dataset:
13
14
  BASE_URL = None
14
- TIMEOUT = 10
15
+ TIMEOUT = 30
15
16
 
16
17
  def __init__(self, project_name):
17
18
  self.project_name = project_name
@@ -63,7 +64,7 @@ class Dataset:
63
64
  sub_datasets = [dataset["name"] for dataset in datasets]
64
65
  return sub_datasets
65
66
 
66
- def create_dataset(self, dataset_name, filter_list):
67
+ def create_from_trace(self, dataset_name, filter_list):
67
68
  """
68
69
  Creates a new dataset with the given `dataset_name` and `filter_list`.
69
70
 
@@ -79,7 +80,7 @@ class Dataset:
79
80
 
80
81
  """
81
82
 
82
- def make_request():
83
+ def request_trace_creation():
83
84
  headers = {
84
85
  "Content-Type": "application/json",
85
86
  "Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
@@ -98,12 +99,129 @@ class Dataset:
98
99
  )
99
100
  return response
100
101
 
101
- response = make_request()
102
+ response = request_trace_creation()
102
103
  response_checker(response, "Dataset.create_dataset")
103
104
  if response.status_code == 401:
104
105
  get_token() # Fetch a new token and set it in the environment
105
- response = make_request() # Retry the request
106
+ response = request_trace_creation() # Retry the request
106
107
  if response.status_code != 200:
107
108
  return response.json()["message"]
108
109
  message = response.json()["message"]
109
110
  return f"{message} {dataset_name}"
111
+
112
+
113
+
114
+ ###################### CSV Upload APIs ###################
115
+
116
+ def get_csv_schema(self):
117
+ headers = {
118
+ "Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
119
+ "X-Project-Name": self.project_name,
120
+ }
121
+ response = requests.get(
122
+ f"{Dataset.BASE_URL}/v1/llm/schema-elements",
123
+ headers=headers,
124
+ timeout=Dataset.TIMEOUT,
125
+ )
126
+
127
+ response_data = response.json()
128
+ if not response_data['success']:
129
+ raise ValueError('Unable to fetch Schema Elements for the CSV')
130
+
131
+ # chema_elements = response['data']['schemaElements']
132
+ return response_data
133
+
134
+
135
+ def create_from_csv(self, csv_path, dataset_name, schema_mapping):
136
+
137
+ ## check the validity of schema_mapping
138
+ df = pd.read_csv(csv_path)
139
+ keys = list(df.columns)
140
+ values = self.get_csv_schema()['data']['schemaElements']
141
+ print(type(values), values)
142
+ for k in schema_mapping.keys():
143
+ if k not in keys:
144
+ raise ValueError(f'--{k}-- column is not present in csv column but present in schema_mapping. Plase provide the right schema_mapping.')
145
+ for k in schema_mapping.values():
146
+ if k not in values:
147
+ raise ValueError(f'--{k}-- is not present in the schema_elements but present in schema_mapping. Plase provide the right schema_mapping.')
148
+
149
+
150
+ #### get presigned URL
151
+ def get_presignedUrl():
152
+ headers = {
153
+ "Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
154
+ "X-Project-Name": self.project_name,
155
+ }
156
+ response = requests.get(
157
+ f"{Dataset.BASE_URL}/v1/llm/presignedUrl/test-url",
158
+ headers=headers,
159
+ timeout=Dataset.TIMEOUT,
160
+ )
161
+ return response.json()
162
+
163
+ presignedUrl = get_presignedUrl()
164
+ if presignedUrl['success']:
165
+ url = presignedUrl['data']['presignedUrl']
166
+ filename = presignedUrl['data']['fileName']
167
+ print('-- PresignedUrl fetched Succussfuly --')
168
+ print('filename: ', filename)
169
+ else:
170
+ raise ValueError('Unable to fetch presignedUrl')
171
+
172
+
173
+
174
+ #### put csv to presigned URL
175
+ def put_csv_to_presignedUrl(url):
176
+ headers = {
177
+ 'Content-Type': 'text/csv',
178
+ 'x-ms-blob-type': 'BlockBlob',
179
+ }
180
+ with open(csv_path, 'rb') as file:
181
+ response = requests.put(
182
+ url,
183
+ headers=headers,
184
+ data=file,
185
+ timeout=Dataset.TIMEOUT,
186
+ )
187
+ return response
188
+
189
+
190
+
191
+ put_csv_response = put_csv_to_presignedUrl(url)
192
+ if put_csv_response.status_code != 201:
193
+ raise ValueError('Unable to put csv to the presignedUrl')
194
+ else:
195
+ print('-- csv put to presignedUrl Succussfuly --')
196
+
197
+
198
+
199
+ ## Upload csv to elastic
200
+ def upload_csv_to_elastic(data):
201
+ header = {
202
+ 'Authorization': f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
203
+ 'X-Project-Name': self.project_name
204
+ }
205
+ response = requests.post(
206
+ f"{Dataset.BASE_URL}/v1/llm/csv-dataset",
207
+ headers=header,
208
+ json=data,
209
+ timeout=Dataset.TIMEOUT,
210
+ )
211
+
212
+ return response.json()
213
+
214
+ data = {
215
+ "datasetName": dataset_name,
216
+ "fileName": filename,
217
+ "schemaMapping": schema_mapping
218
+ }
219
+ print(data)
220
+
221
+ upload_csv_response = upload_csv_to_elastic(data)
222
+ print(type(upload_csv_response), upload_csv_response)
223
+ if not upload_csv_response['success']:
224
+ raise ValueError('Unable to upload csv')
225
+ else:
226
+ print(upload_csv_response['message'])
227
+
@@ -14,6 +14,7 @@ get_token = RagaAICatalyst.get_token
14
14
  class Experiment:
15
15
  BASE_URL = None
16
16
  TIMEOUT = 10
17
+ NUM_PROJECTS = 100
17
18
 
18
19
  def __init__(
19
20
  self, project_name, experiment_name, experiment_description, dataset_name
@@ -42,6 +43,28 @@ class Experiment:
42
43
  self.experiment_id = None
43
44
  self.job_id = None
44
45
 
46
+ params = {
47
+ "size": str(self.NUM_PROJECTS),
48
+ "page": "0",
49
+ "type": "llm",
50
+ }
51
+ headers = {
52
+ "Content-Type": "application/json",
53
+ "Authorization": f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
54
+ }
55
+ response = requests.get(
56
+ f"{RagaAICatalyst.BASE_URL}/projects",
57
+ params=params,
58
+ headers=headers,
59
+ timeout=10,
60
+ )
61
+ response.raise_for_status()
62
+ # logger.debug("Projects list retrieved successfully")
63
+ experiment_list = [exp["name"] for project in response.json()["data"]["content"] if project["name"] == self.project_name for exp in project["experiments"]]
64
+ # print(experiment_list)
65
+ if self.experiment_name in experiment_list:
66
+ raise ValueError("The experiment name already exists in the project. Enter a unique experiment name.")
67
+
45
68
  self.access_key = os.getenv("RAGAAI_CATALYST_ACCESS_KEY")
46
69
  self.secret_key = os.getenv("RAGAAI_CATALYST_SECRET_KEY")
47
70
 
@@ -50,8 +73,73 @@ class Experiment:
50
73
  if os.getenv("RAGAAI_CATALYST_TOKEN") is not None
51
74
  else get_token()
52
75
  )
76
+
77
+ if not self._check_if_project_exists(project_name=project_name):
78
+ raise ValueError(f"Project '{project_name}' not found. Please enter a valid project name")
79
+
80
+ if not self._check_if_dataset_exists(project_name=project_name,dataset_name=dataset_name):
81
+ raise ValueError(f"dataset '{dataset_name}' not found. Please enter a valid dataset name")
82
+
83
+
53
84
  self.metrics = []
85
+ def _check_if_dataset_exists(self,project_name,dataset_name):
86
+ headers = {
87
+ "X-Project-Name":project_name,
88
+ # "accept":"application/json, text/plain, */*",
89
+ "Authorization": f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
90
+ }
91
+ response = requests.get(
92
+ f"{RagaAICatalyst.BASE_URL}/v1/llm/sub-datasets?projectName={project_name}",
93
+ headers=headers,
94
+ timeout=self.TIMEOUT,
95
+ )
96
+ response.raise_for_status()
97
+ logger.debug("dataset list retrieved successfully")
98
+ dataset_list = [
99
+ item['name'] for item in response.json()['data']['content']
100
+ ]
101
+ exists = dataset_name in dataset_list
102
+ if exists:
103
+ logger.info(f"dataset '{dataset_name}' exists.")
104
+ else:
105
+ logger.info(f"dataset '{dataset_name}' does not exist.")
106
+ return exists
107
+
108
+
54
109
 
110
+
111
+ def _check_if_project_exists(self,project_name,num_projects=100):
112
+ # TODO: 1. List All projects
113
+ params = {
114
+ "size": str(num_projects),
115
+ "page": "0",
116
+ "type": "llm",
117
+ }
118
+ headers = {
119
+ "Content-Type": "application/json",
120
+ "Authorization": f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
121
+ }
122
+ response = requests.get(
123
+ f"{RagaAICatalyst.BASE_URL}/projects",
124
+ params=params,
125
+ headers=headers,
126
+ timeout=self.TIMEOUT,
127
+ )
128
+ response.raise_for_status()
129
+ logger.debug("Projects list retrieved successfully")
130
+ project_list = [
131
+ project["name"] for project in response.json()["data"]["content"]
132
+ ]
133
+
134
+ # TODO: 2. Check if the given project_name exists
135
+ # TODO: 3. Return bool (True / False output)
136
+ exists = project_name in project_list
137
+ if exists:
138
+ logger.info(f"Project '{project_name}' exists.")
139
+ else:
140
+ logger.info(f"Project '{project_name}' does not exist.")
141
+ return exists
142
+
55
143
  def list_experiments(self):
56
144
  """
57
145
  Retrieves a list of experiments associated with the current project.
@@ -115,6 +203,15 @@ class Experiment:
115
203
  metrics = [metrics]
116
204
  else:
117
205
  metrics_list = metrics
206
+ sub_providers = ["openai","azure","gemini","groq"]
207
+ sub_metrics = RagaAICatalyst.list_metrics()
208
+ for metric in metrics_list:
209
+ provider = metric.get('config', {}).get('provider', '').lower()
210
+ if provider and provider not in sub_providers:
211
+ raise ValueError("Enter a valid provider name. The following Provider names are supported: OpenAI, Azure, Gemini, Groq")
212
+
213
+ if metric['name'] not in sub_metrics:
214
+ raise ValueError("Enter a valid metric name. Refer to RagaAI Metric Library to select a valid metric")
118
215
 
119
216
  json_data = {
120
217
  "projectName": self.project_name,
@@ -274,14 +371,15 @@ class Experiment:
274
371
  "projectId": self.project_id,
275
372
  "filterList": [],
276
373
  }
374
+ base_url_without_api = Experiment.BASE_URL.removesuffix('/api')
277
375
 
278
376
  status_json = self.get_status(job_id_to_use)
279
377
  if status_json == "Failed":
280
378
  return print("Job failed. No results to fetch.")
281
379
  elif status_json == "In Progress":
282
- return print("Job in progress. Please wait while the job completes.")
380
+ return print(f"Job in progress. Please wait while the job completes.\n Visit Job Status: {base_url_without_api}/home/job-status to track")
283
381
  elif status_json == "Completed":
284
- print("Job completed. fetching results")
382
+ print(f"Job completed. fetching results.\n Visit Job Status: {base_url_without_api}/home/job-status to track")
285
383
 
286
384
  response = requests.post(
287
385
  f"{Experiment.BASE_URL}/v1/llm/docs",
@@ -366,9 +464,6 @@ class Experiment:
366
464
  y.get("metric_config") if isinstance(y, dict) else None
367
465
  )
368
466
  )
369
- x[f"{dict_col}_passed"] = x[dict_col].apply(
370
- lambda y: y.get("passed") if isinstance(y, dict) else None
371
- )
372
467
  x[f"{dict_col}_status"] = x[dict_col].apply(
373
468
  lambda y: y.get("status") if isinstance(y, dict) else None
374
469
  )
@@ -377,8 +472,10 @@ class Experiment:
377
472
 
378
473
  x.columns = x.columns.str.replace("_reason_reason", "_reason")
379
474
  x.columns = x.columns.str.replace("_reason_metric_config", "_metric_config")
380
- x.columns = x.columns.str.replace("_reason_passed", "_passed")
381
475
  x.columns = x.columns.str.replace("_reason_status", "_status")
476
+
477
+ x = x.drop(columns=["trace_uri"])
478
+
382
479
  return True, x
383
480
 
384
481
  except Exception as e:
@@ -0,0 +1,382 @@
1
+ import os
2
+ import requests
3
+ import json
4
+ import re
5
+ from .ragaai_catalyst import RagaAICatalyst
6
+ import pdb
7
+
8
+ class PromptManager:
9
+ NUM_PROJECTS = 100
10
+ TIMEOUT = 10
11
+
12
+ def __init__(self, project_name):
13
+ """
14
+ Initialize the PromptManager with a project name.
15
+
16
+ Args:
17
+ project_name (str): The name of the project.
18
+
19
+ Raises:
20
+ requests.RequestException: If there's an error with the API request.
21
+ ValueError: If the project is not found.
22
+ """
23
+ self.project_name = project_name
24
+ self.headers = {
25
+ "Content-Type": "application/json",
26
+ "Authorization": f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
27
+ "X-Project-Name": self.project_name
28
+ }
29
+ self.base_url = f"{RagaAICatalyst.BASE_URL}/playground/prompt"
30
+ self.timeout = 10
31
+
32
+ try:
33
+ response = requests.get(
34
+ f"{RagaAICatalyst.BASE_URL}/projects",
35
+ params={
36
+ "size": str(self.NUM_PROJECTS),
37
+ "page": "0",
38
+ "type": "llm",
39
+ },
40
+ headers={
41
+ "Content-Type": "application/json",
42
+ "Authorization": f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
43
+ },
44
+ timeout=self.TIMEOUT,
45
+ )
46
+ response.raise_for_status()
47
+ except requests.RequestException as e:
48
+ raise requests.RequestException(f"Error fetching projects: {str(e)}")
49
+
50
+ try:
51
+ project_list = [
52
+ project["name"] for project in response.json()["data"]["content"]
53
+ ]
54
+ except (KeyError, json.JSONDecodeError) as e:
55
+ raise ValueError(f"Error parsing project list: {str(e)}")
56
+
57
+ if self.project_name not in project_list:
58
+ raise ValueError("Project not found. Please enter a valid project name")
59
+
60
+
61
+ def list_prompts(self):
62
+ """
63
+ List all available prompts.
64
+
65
+ Returns:
66
+ list: A list of prompt names.
67
+
68
+ Raises:
69
+ requests.RequestException: If there's an error with the API request.
70
+ """
71
+ prompt = Prompt()
72
+ try:
73
+ prompt_list = prompt.list_prompts(self.base_url, self.headers, self.timeout)
74
+ return prompt_list
75
+ except requests.RequestException as e:
76
+ raise requests.RequestException(f"Error listing prompts: {str(e)}")
77
+
78
+ def get_prompt(self, prompt_name, version=None):
79
+ """
80
+ Get a specific prompt.
81
+
82
+ Args:
83
+ prompt_name (str): The name of the prompt.
84
+ version (str, optional): The version of the prompt. Defaults to None.
85
+
86
+ Returns:
87
+ PromptObject: An object representing the prompt.
88
+
89
+ Raises:
90
+ ValueError: If the prompt or version is not found.
91
+ requests.RequestException: If there's an error with the API request.
92
+ """
93
+ try:
94
+ prompt_list = self.list_prompts()
95
+ except requests.RequestException as e:
96
+ raise requests.RequestException(f"Error fetching prompt list: {str(e)}")
97
+
98
+ if prompt_name not in prompt_list:
99
+ raise ValueError("Prompt not found. Please enter a valid prompt name")
100
+
101
+ try:
102
+ prompt_versions = self.list_prompt_versions(prompt_name)
103
+ except requests.RequestException as e:
104
+ raise requests.RequestException(f"Error fetching prompt versions: {str(e)}")
105
+
106
+ if version and version not in prompt_versions.keys():
107
+ raise ValueError("Version not found. Please enter a valid version name")
108
+
109
+ prompt = Prompt()
110
+ try:
111
+ prompt_object = prompt.get_prompt(self.base_url, self.headers, self.timeout, prompt_name, version)
112
+ return prompt_object
113
+ except requests.RequestException as e:
114
+ raise requests.RequestException(f"Error fetching prompt: {str(e)}")
115
+
116
+ def list_prompt_versions(self, prompt_name):
117
+ """
118
+ List all versions of a specific prompt.
119
+
120
+ Args:
121
+ prompt_name (str): The name of the prompt.
122
+
123
+ Returns:
124
+ dict: A dictionary mapping version names to prompt texts.
125
+
126
+ Raises:
127
+ ValueError: If the prompt is not found.
128
+ requests.RequestException: If there's an error with the API request.
129
+ """
130
+ try:
131
+ prompt_list = self.list_prompts()
132
+ except requests.RequestException as e:
133
+ raise requests.RequestException(f"Error fetching prompt list: {str(e)}")
134
+
135
+ if prompt_name not in prompt_list:
136
+ raise ValueError("Prompt not found. Please enter a valid prompt name")
137
+
138
+ prompt = Prompt()
139
+ try:
140
+ prompt_versions = prompt.list_prompt_versions(self.base_url, self.headers, self.timeout, prompt_name)
141
+ return prompt_versions
142
+ except requests.RequestException as e:
143
+ raise requests.RequestException(f"Error fetching prompt versions: {str(e)}")
144
+
145
+
146
+ class Prompt:
147
+ def __init__(self):
148
+ """
149
+ Initialize the Prompt class.
150
+ """
151
+ pass
152
+
153
+ def list_prompts(self, url, headers, timeout):
154
+ """
155
+ List all available prompts.
156
+
157
+ Args:
158
+ url (str): The base URL for the API.
159
+ headers (dict): The headers to be used in the request.
160
+ timeout (int): The timeout for the request.
161
+
162
+ Returns:
163
+ list: A list of prompt names.
164
+
165
+ Raises:
166
+ requests.RequestException: If there's an error with the API request.
167
+ """
168
+ try:
169
+ response = requests.get(url, headers=headers, timeout=timeout)
170
+ response.raise_for_status()
171
+ prompt_list = [prompt["name"] for prompt in response.json()["data"]]
172
+ return prompt_list
173
+ except requests.RequestException as e:
174
+ raise requests.RequestException(f"Error listing prompts: {str(e)}")
175
+ except (KeyError, json.JSONDecodeError) as e:
176
+ raise ValueError(f"Error parsing prompt list: {str(e)}")
177
+
178
+ def get_response_by_version(self, base_url, headers, timeout, prompt_name, version):
179
+ """
180
+ Get a specific version of a prompt.
181
+
182
+ Args:
183
+ base_url (str): The base URL for the API.
184
+ headers (dict): The headers to be used in the request.
185
+ timeout (int): The timeout for the request.
186
+ prompt_name (str): The name of the prompt.
187
+ version (str): The version of the prompt."""
188
+ try:
189
+ response = requests.get(f"{base_url}/version/{prompt_name}?version={version}",
190
+ headers=headers, timeout=timeout)
191
+ response.raise_for_status()
192
+ except requests.RequestException as e:
193
+ raise requests.RequestException(f"Error fetching prompt version: {str(e)}")
194
+ except (KeyError, json.JSONDecodeError, IndexError) as e:
195
+ raise ValueError(f"Error parsing prompt version: {str(e)}")
196
+ return response
197
+
198
+ def get_response(self, base_url, headers, timeout, prompt_name, version):
199
+ try:
200
+ response = requests.get(f"{base_url}/version/{prompt_name}",
201
+ headers=headers, timeout=timeout)
202
+ response.raise_for_status()
203
+ except requests.RequestException as e:
204
+ raise requests.RequestException(f"Error fetching prompt version: {str(e)}")
205
+ except (KeyError, json.JSONDecodeError, IndexError) as e:
206
+ raise ValueError(f"Error parsing prompt version: {str(e)}")
207
+ return response
208
+
209
+ def get_prompt_by_version(self, base_url, headers, timeout, prompt_name, version):
210
+ """
211
+ Get a specific version of a prompt.
212
+
213
+ Args:
214
+ base_url (str): The base URL for the API.
215
+ headers (dict): The headers to be used in the request.
216
+ timeout (int): The timeout for the request.
217
+ prompt_name (str): The name of the prompt.
218
+ version (str): The version of the prompt.
219
+
220
+ Returns:
221
+ str: The text of the prompt.
222
+
223
+ Raises:
224
+ requests.RequestException: If there's an error with the API request.
225
+ """
226
+ response = self.get_response_by_version(base_url, headers, timeout, prompt_name, version)
227
+ prompt_text = response.json()["data"]["docs"][0]["textFields"]
228
+ return prompt_text
229
+
230
+ def get_prompt(self, base_url, headers, timeout, prompt_name, version=None):
231
+ """
232
+ Get a prompt, optionally specifying a version.
233
+
234
+ Args:
235
+ base_url (str): The base URL for the API.
236
+ headers (dict): The headers to be used in the request.
237
+ timeout (int): The timeout for the request.
238
+ prompt_name (str): The name of the prompt.
239
+ version (str, optional): The version of the prompt. Defaults to None.
240
+
241
+ Returns:
242
+ PromptObject: An object representing the prompt.
243
+
244
+ Raises:
245
+ requests.RequestException: If there's an error with the API request.
246
+ """
247
+ if version:
248
+ response = self.get_response_by_version(base_url, headers, timeout, prompt_name, version)
249
+ prompt_text = response.json()["data"]["docs"][0]["textFields"]
250
+ prompt_parameters = response.json()["data"]["docs"][0]["modelSpecs"]["parameters"]
251
+ model = response.json()["data"]["docs"][0]["modelSpecs"]["model"]
252
+ else:
253
+ response = self.get_response(base_url, headers, timeout, prompt_name)
254
+ prompt_text = response.json()["data"]["docs"][0]["textFields"]
255
+ prompt_parameters = response.json()["data"]["docs"][0]["modelSpecs"]["parameters"]
256
+ model = response.json()["data"]["docs"][0]["modelSpecs"]["model"]
257
+ return PromptObject(prompt_text, prompt_parameters, model)
258
+
259
+
260
+ def list_prompt_versions(self, base_url, headers, timeout, prompt_name):
261
+ """
262
+ List all versions of a specific prompt.
263
+
264
+ Args:
265
+ base_url (str): The base URL for the API.
266
+ headers (dict): The headers to be used in the request.
267
+ timeout (int): The timeout for the request.
268
+ prompt_name (str): The name of the prompt.
269
+
270
+ Returns:
271
+ dict: A dictionary mapping version names to prompt texts.
272
+
273
+ Raises:
274
+ requests.RequestException: If there's an error with the API request.
275
+ """
276
+ try:
277
+ response = requests.get(f"{base_url}/{prompt_name}/version",
278
+ headers=headers, timeout=timeout)
279
+ response.raise_for_status()
280
+ version_names = [version["name"] for version in response.json()["data"]]
281
+ prompt_versions = {}
282
+ for version in version_names:
283
+ prompt_versions[version] = self.get_prompt_by_version(base_url, headers, timeout, prompt_name, version)
284
+ return prompt_versions
285
+ except requests.RequestException as e:
286
+ raise requests.RequestException(f"Error listing prompt versions: {str(e)}")
287
+ except (KeyError, json.JSONDecodeError) as e:
288
+ raise ValueError(f"Error parsing prompt versions: {str(e)}")
289
+
290
+
291
+ class PromptObject:
292
+ def __init__(self, text, parameters, model):
293
+ """
294
+ Initialize a PromptObject with the given text.
295
+
296
+ Args:
297
+ text (str): The text of the prompt.
298
+ parameters (dict): The parameters of the prompt.
299
+ model (str): The model of the prompt.
300
+ """
301
+ self.text = text
302
+ self.variables = self._extract_variables()
303
+ self.parameters = parameters
304
+ self.model = model
305
+
306
+ def _extract_variables(self):
307
+ """
308
+ Extract variables from the prompt text.
309
+
310
+ Returns:
311
+ list: A list of variable names found in the prompt text.
312
+ """
313
+ user_content = next(item["content"] for item in self.text if item["role"] == "user")
314
+ return [var.strip('{}') for var in user_content.split('{{')[1:]]
315
+
316
+ def compile(self, **kwargs):
317
+ """
318
+ Compile the prompt by replacing variables with provided values.
319
+
320
+ Args:
321
+ **kwargs: Keyword arguments where keys are variable names and values are their replacements.
322
+
323
+ Returns:
324
+ str: The compiled prompt with variables replaced.
325
+
326
+ Raises:
327
+ ValueError: If there are missing or extra variables, or if a value is not a string.
328
+ """
329
+ required_variables = set(self.get_variables())
330
+ provided_variables = set(kwargs.keys())
331
+
332
+ missing_variables = required_variables - provided_variables
333
+ extra_variables = provided_variables - required_variables
334
+
335
+ if missing_variables:
336
+ raise ValueError(f"Missing variable(s): {', '.join(missing_variables)}")
337
+ if extra_variables:
338
+ raise ValueError(f"Extra variable(s) provided: {', '.join(extra_variables)}")
339
+ # pdb.set_trace()
340
+
341
+ # compiled_prompt = self.text
342
+ user_content = next(item["content"] for item in self.text if item["role"] == "user")
343
+
344
+ for key, value in kwargs.items():
345
+ if not isinstance(value, str):
346
+ raise ValueError(f"Value for variable '{key}' must be a string, not {type(value).__name__}")
347
+ user_content = user_content.replace(f"{{{{{key}}}}}", value)
348
+ compiled_prompt = [{"content": user_content if item["role"] == "user" else item["content"], "role": item["role"]} for item in self.text]
349
+ return compiled_prompt
350
+
351
+ def get_variables(self):
352
+ """
353
+ Get all variables in the prompt text.
354
+
355
+ Returns:
356
+ list: A list of variable names found in the prompt text.
357
+ """
358
+ pattern = r'\{\{(.*?)\}\}'
359
+ user_content = next(item["content"] for item in self.text if item["role"] == "user")
360
+ matches = re.findall(pattern, user_content)
361
+ return [match.strip() for match in matches if '"' not in match]
362
+
363
+ # Function to convert value based on type
364
+ def convert_value(self, value, type_):
365
+ if type_ == "float":
366
+ return float(value)
367
+ elif type_ == "int":
368
+ return int(value)
369
+ return value # Default case, return as is
370
+
371
+ def get_parameters(self):
372
+ """
373
+ Get all parameters in the prompt text.
374
+
375
+ Returns:
376
+ dict: A dictionary of parameters found in the prompt text.
377
+ """
378
+ parameters = {param["name"]: self.convert_value(param["value"], param["type"]) for param in self.parameters}
379
+ parameters["model"] = self.model
380
+ return parameters
381
+
382
+
@@ -24,16 +24,15 @@ class RagaAICatalyst:
24
24
  access_key (str): The access key for the RagaAICatalyst.
25
25
  secret_key (str): The secret key for the RagaAICatalyst.
26
26
  api_keys (Optional[Dict[str, str]]): A dictionary of API keys for different services. Defaults to None.
27
+ base_url (Optional[str]): The base URL for the RagaAICatalyst API. Defaults to None.
27
28
 
28
29
  Raises:
29
30
  ValueError: If the RAGAAI_CATALYST_ACCESS_KEY and RAGAAI_CATALYST_SECRET_KEY environment variables are not set.
31
+ ConnectionError: If the provided base_url is not accessible.
30
32
 
31
33
  Returns:
32
34
  None
33
35
  """
34
- if base_url:
35
- RagaAICatalyst.BASE_URL = base_url
36
- os.environ["RAGAAI_CATALYST_BASE_URL"] = base_url
37
36
 
38
37
  if not access_key or not secret_key:
39
38
  logger.error(
@@ -46,22 +45,25 @@ class RagaAICatalyst:
46
45
  self.access_key, self.secret_key = self._set_access_key_secret_key(
47
46
  access_key, secret_key
48
47
  )
48
+
49
49
  RagaAICatalyst.BASE_URL = (
50
50
  os.getenv("RAGAAI_CATALYST_BASE_URL")
51
51
  if os.getenv("RAGAAI_CATALYST_BASE_URL")
52
- else "https://llm-platform.dev4.ragaai.ai/api"
52
+ else "https://catalyst.raga.ai/api"
53
53
  )
54
- os.environ["RAGAAI_CATALYST_ACCESS_KEY"] = access_key
55
- os.environ["RAGAAI_CATALYST_SECRET_KEY"] = secret_key
56
54
 
57
55
  self.api_keys = api_keys or {}
58
- self.get_token()
56
+
59
57
  if self.api_keys:
60
58
  self._upload_keys()
61
59
 
62
60
  if base_url:
63
61
  RagaAICatalyst.BASE_URL = base_url
64
- os.environ["RAGAAI_CATALYST_BASE_URL"] = base_url
62
+ try:
63
+ self.get_token()
64
+ os.environ["RAGAAI_CATALYST_BASE_URL"] = base_url
65
+ except requests.exceptions.RequestException:
66
+ raise ConnectionError("The provided base_url is not accessible. Please re-check the base_url.")
65
67
 
66
68
  def _set_access_key_secret_key(self, access_key, secret_key):
67
69
  os.environ["RAGAAI_CATALYST_ACCESS_KEY"] = access_key
@@ -130,7 +132,7 @@ class RagaAICatalyst:
130
132
  Raises:
131
133
  - requests.exceptions.HTTPError: If there is an HTTP error while retrieving the token.
132
134
  - requests.exceptions.RequestException: If there is an error while retrieving the token.
133
- - ValueError: If there is a JSON decoding error.
135
+ - ValueError: If there is a JSON decoding error or if authentication fails.
134
136
  - Exception: If there is an unexpected error while retrieving the token.
135
137
  """
136
138
  access_key = os.getenv("RAGAAI_CATALYST_ACCESS_KEY")
@@ -145,54 +147,43 @@ class RagaAICatalyst:
145
147
  headers = {"Content-Type": "application/json"}
146
148
  json_data = {
147
149
  "accessKey": access_key,
148
- "secretKey": secret_key,
150
+ "secretKey": secret_key
149
151
  }
150
152
 
151
- try:
152
- response = requests.post(
153
- f"{ RagaAICatalyst.BASE_URL}/token",
154
- headers=headers,
155
- json=json_data,
156
- timeout=RagaAICatalyst.TIMEOUT,
157
- )
158
- response.raise_for_status()
153
+ response = requests.post(
154
+ f"{ RagaAICatalyst.BASE_URL}/token",
155
+ headers=headers,
156
+ json=json_data,
157
+ timeout=RagaAICatalyst.TIMEOUT,
158
+ )
159
159
 
160
+ # Handle specific status codes before raising an error
161
+ if response.status_code == 400:
160
162
  token_response = response.json()
163
+ if token_response.get("message") == "Please enter valid credentials":
164
+ raise Exception("Authentication failed. Invalid credentials provided. Please check your Access key and Secret key. \nTo view or create new keys, navigate to Settings -> Authenticate in the RagaAI Catalyst dashboard.")
161
165
 
162
- if not token_response.get("success", False):
163
- logger.error(
164
- "Token retrieval was not successful: %s",
165
- token_response.get("message", "Unknown error"),
166
- )
167
- return None
166
+ response.raise_for_status()
168
167
 
169
- token = token_response.get("data", {}).get("token")
170
- if token:
171
- os.environ["RAGAAI_CATALYST_TOKEN"] = token
172
- print("Token(s) set successfully")
173
- return token
174
- else:
175
- logger.error("Token(s) not set")
176
- return None
168
+ token_response = response.json()
177
169
 
178
- except requests.exceptions.HTTPError as http_err:
170
+ if not token_response.get("success", False):
179
171
  logger.error(
180
- "HTTP error occurred while retrieving token: %s", str(http_err)
172
+ "Token retrieval was not successful: %s",
173
+ token_response.get("message", "Unknown error"),
181
174
  )
182
- if response.status_code == 500:
183
- error_message = response.json().get("message", "Unknown server error")
184
- logger.error("Server error: %s", error_message)
185
- return None
186
- except requests.exceptions.RequestException as req_err:
187
- logger.error("Error occurred while retrieving token: %s", str(req_err))
188
- return None
189
- except ValueError as json_err:
190
- logger.error("JSON decoding error: %s", str(json_err))
191
175
  return None
192
- except Exception as e:
193
- logger.error("Unexpected error occurred while retrieving token: %s", str(e))
176
+
177
+ token = token_response.get("data", {}).get("token")
178
+ if token:
179
+ os.environ["RAGAAI_CATALYST_TOKEN"] = token
180
+ print("Token(s) set successfully")
181
+ return token
182
+ else:
183
+ logger.error("Token(s) not set")
194
184
  return None
195
185
 
186
+
196
187
  def create_project(self, project_name, type="llm", description=""):
197
188
  """
198
189
  Creates a project with the given project_name, type, and description.
@@ -262,6 +253,7 @@ class RagaAICatalyst:
262
253
  "Unexpected error while creating project: %s", str(general_err1)
263
254
  )
264
255
  return "An unexpected error occurred while creating the project"
256
+
265
257
 
266
258
  def list_projects(self, num_projects=100):
267
259
  """
@@ -344,6 +336,10 @@ class RagaAICatalyst:
344
336
  return "An unexpected error occurred while listing projects"
345
337
 
346
338
  def list_metrics(self):
339
+ return RagaAICatalyst.list_metrics()
340
+
341
+ @staticmethod
342
+ def list_metrics():
347
343
  headers = {
348
344
  "Content-Type": "application/json",
349
345
  "Authorization": f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
@@ -352,7 +348,7 @@ class RagaAICatalyst:
352
348
  response = requests.get(
353
349
  f"{RagaAICatalyst.BASE_URL}/v1/llm/llm-metrics",
354
350
  headers=headers,
355
- timeout=self.TIMEOUT,
351
+ timeout=RagaAICatalyst.TIMEOUT,
356
352
  )
357
353
  response.raise_for_status()
358
354
  logger.debug("Metrics list retrieved successfully")
@@ -44,7 +44,7 @@ class RagaExporter:
44
44
  RagaExporter.BASE_URL = (
45
45
  os.getenv("RAGAAI_CATALYST_BASE_URL")
46
46
  if os.getenv("RAGAAI_CATALYST_BASE_URL")
47
- else "https://llm-platform.dev4.ragaai.ai/api"
47
+ else "https://catalyst.raga.ai/api"
48
48
  )
49
49
  self.access_key = os.getenv("RAGAAI_CATALYST_ACCESS_KEY")
50
50
  self.secret_key = os.getenv("RAGAAI_CATALYST_SECRET_KEY")
@@ -92,6 +92,23 @@ class RagaExporter:
92
92
  timeout=RagaExporter.TIMEOUT,
93
93
  )
94
94
  return response
95
+
96
+
97
+ def compare_schemas(base_schema, project_schema):
98
+ differences = []
99
+ for key, base_value in base_schema.items():
100
+ if key not in project_schema:
101
+ differences.append(f"Key '{key}' is missing in new schema.")
102
+ else:
103
+ # Remove everything after '_' in the new schema value
104
+ new_value = project_schema[key].split('_')[0]
105
+ if base_value != new_value:
106
+ differences.append(f"Value mismatch for key '{key}': base = '{base_value}', new = '{new_value}'.")
107
+
108
+ if differences:
109
+ return False, differences
110
+ return True, []
111
+
95
112
 
96
113
  response = make_request()
97
114
  if response.status_code == 401:
@@ -99,6 +116,13 @@ class RagaExporter:
99
116
  response = make_request() # Retry the request
100
117
  if response.status_code != 200:
101
118
  return response.status_code
119
+ if response.status_code == 200:
120
+ project_schema = response.json()["data"]
121
+ base_schema = RagaExporter.SCHEMA_MAPPING
122
+ is_same, _ = compare_schemas(base_schema, project_schema)
123
+ if not is_same:
124
+ raise Exception(f"Trace cannot be logged to this Project because of schema difference. Create a new project to log trace")
125
+ return response.status_code
102
126
  return response.status_code
103
127
 
104
128
  def _create_schema(self):
@@ -18,11 +18,13 @@ from .instrumentators import (
18
18
  )
19
19
  from .utils import get_unique_key
20
20
 
21
+ from ..ragaai_catalyst import RagaAICatalyst
22
+
21
23
  logger = logging.getLogger(__name__)
22
24
 
23
25
 
24
26
  class Tracer:
25
-
27
+ NUM_PROJECTS = 100
26
28
  def __init__(
27
29
  self,
28
30
  project_name,
@@ -53,6 +55,31 @@ class Tracer:
53
55
  self.description = description
54
56
  self.upload_timeout = upload_timeout
55
57
 
58
+ params = {
59
+ "size": str(self.NUM_PROJECTS),
60
+ "page": "0",
61
+ "type": "llm",
62
+ }
63
+ headers = {
64
+ "Content-Type": "application/json",
65
+ "Authorization": f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
66
+ }
67
+ response = requests.get(
68
+ f"{RagaAICatalyst.BASE_URL}/projects",
69
+ params=params,
70
+ headers=headers,
71
+ timeout=10,
72
+ )
73
+ response.raise_for_status()
74
+ # logger.debug("Projects list retrieved successfully")
75
+ project_list = [
76
+ project["name"] for project in response.json()["data"]["content"]
77
+ ]
78
+
79
+ if self.project_name not in project_list:
80
+ raise ValueError("Project not found. Please enter a valid project name")
81
+
82
+
56
83
  self.raga_client = RagaExporter(project_name=self.project_name)
57
84
 
58
85
  self._tracer_provider = self._setup_provider()
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ragaai_catalyst
3
- Version: 1.0.7b4
3
+ Version: 1.0.8b2
4
4
  Summary: RAGA AI CATALYST
5
- Author-email: Kiran Scaria <kiran.scaria@raga.ai>, Kedar Gaikwad <kedar.gaikwad@raga.ai>, Dushyant Mahajan <dushyant.mahajan@raga.ai>
5
+ Author-email: Kiran Scaria <kiran.scaria@raga.ai>, Kedar Gaikwad <kedar.gaikwad@raga.ai>, Dushyant Mahajan <dushyant.mahajan@raga.ai>, Siddhartha Kosti <siddhartha.kosti@raga.ai>, Ritika Goel <ritika.goel@raga.ai>, Vijay Chaurasia <vijay.chaurasia@raga.ai>
6
6
  Requires-Python: >=3.9
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: aiohttp>=3.10.2
@@ -44,6 +44,7 @@ RagaAI Catalyst is a powerful tool for managing and optimizing LLM projects. It
44
44
  - [Trace Management](#trace-management)
45
45
  - [Experiment Management](#experiment-management)
46
46
  - [Dataset Management](#dataset-management)
47
+ - [Prompt Management](#prompt-management)
47
48
 
48
49
  ## Installation
49
50
 
@@ -63,9 +64,11 @@ from ragaai_catalyst import RagaAICatalyst
63
64
  catalyst = RagaAICatalyst(
64
65
  access_key="YOUR_ACCESS_KEY",
65
66
  secret_key="YOUR_SECRET_KEY",
66
- api_keys={"OPENAI_API_KEY": "YOUR_OPENAI_API_KEY"}
67
+ base_url="BASE_URL"
67
68
  )
68
69
  ```
70
+ **Note**: Authetication to RagaAICatalyst is necessary to perform any operations below
71
+
69
72
 
70
73
  ## Usage
71
74
 
@@ -102,12 +105,12 @@ tracer = Tracer(
102
105
  "vector_store": "faiss",
103
106
  "embed_model": "text-embedding-ada-002",
104
107
  }
105
- ).start_trace()
108
+ ).start()
106
109
 
107
110
  # Your code here
108
111
 
109
112
  # Stop the trace recording
110
- tracer.stop_trace()
113
+ tracer.stop()
111
114
 
112
115
  # Alternatively, use a context manager
113
116
  with tracer.trace():
@@ -153,34 +156,69 @@ print("Experiment Status:", status)
153
156
  results = experiment_manager.get_results()
154
157
  print("Experiment Results:", results)
155
158
  ```
156
- ### Dataset Management
157
159
 
158
- Create and manage trace datasets for your projects.
159
160
 
160
161
 
161
- ```python
162
+ ## Dataset Management
163
+ Manage datasets efficiently for your projects:
164
+
165
+ ```py
162
166
  from ragaai_catalyst import Dataset
163
167
 
164
168
  # Initialize Dataset management for a specific project
165
- dataset_manager = Dataset(project_name="Test-RAG-App-1")
169
+ dataset_manager = Dataset(project_name="project_name")
166
170
 
167
171
  # List existing datasets
168
172
  datasets = dataset_manager.list_datasets()
169
- print("Exisiting Datasets:", datasets)
173
+ print("Existing Datasets:", datasets)
170
174
 
171
- # Create a new dataset with filters
172
- dataset_manager.create_dataset(
175
+ # Create a dataset from trace
176
+ dataset_manager.create_from_trace(
173
177
  dataset_name='Test-dataset-1',
174
178
  filter_list=[
175
- {
176
- "name": "llm_model",
177
- "values": ["gpt-3.5-turbo", "gpt-4"]
178
- },
179
- {
180
- "name": "prompt_length",
181
- "lte": 27,
182
- "gte": 23
183
- }
179
+ {"name": "llm_model", "values": ["gpt-3.5-turbo", "gpt-4"]},
180
+ {"name": "prompt_length", "lte": 27, "gte": 23}
184
181
  ]
185
182
  )
183
+
184
+ # Create a dataset from CSV
185
+ dataset_manager.create_from_csv(
186
+ csv_path='path/to/your.csv',
187
+ dataset_name='MyDataset',
188
+ schema_mapping={'column1': 'schema_element1', 'column2': 'schema_element2'}
189
+ )
186
190
  ```
191
+
192
+ For more detailed information on Dataset Management, including CSV schema handling and advanced usage, please refer to the [Dataset Management documentation](docs/dataset_management.md).
193
+
194
+ ## Prompt Management
195
+
196
+ Manage and use prompts efficiently in your projects:
197
+
198
+ ```py
199
+ from ragaai_catalyst.prompt_manager import PromptManager
200
+
201
+ # Initialize PromptManager
202
+ prompt_manager = PromptManager("your-project-name")
203
+
204
+ # List available prompts
205
+ prompts = prompt_manager.list_prompts()
206
+ print("Available prompts:", prompts)
207
+
208
+ # Get a specific prompt
209
+ prompt_name = "your_prompt_name"
210
+ prompt = prompt_manager.get_prompt(prompt_name)
211
+
212
+ # Compile a prompt with variables
213
+ compiled_prompt = prompt.compile(query="What's the weather?", context="sunny", llm_response="It's sunny today")
214
+ print("Compiled prompt:", compiled_prompt)
215
+
216
+ # Get prompt parameters
217
+ parameters = prompt.get_parameters()
218
+ print("Prompt parameters:", parameters)
219
+ ```
220
+
221
+ For more detailed information on Prompt Management, please refer to the [Prompt Management documentation](docs/prompt_management.md).
222
+
223
+
224
+
@@ -1,21 +1,22 @@
1
1
  ragaai_catalyst/__init__.py,sha256=0hf_H1xkhrpGnUWTbjvYOXXQta1S4JO2yud1Eqydt-I,226
2
2
  ragaai_catalyst/_version.py,sha256=JKt9KaVNOMVeGs8ojO6LvIZr7ZkMzNN-gCcvryy4x8E,460
3
- ragaai_catalyst/dataset.py,sha256=g0YH0QGDpCiJ4VULB3tukGFNLpWGw0ZRwLMP07ay2IE,3560
4
- ragaai_catalyst/experiment.py,sha256=7b81BGzZroUArS6UIsluNR6NWJSOcH8M4tFKiRRSoKc,14862
5
- ragaai_catalyst/ragaai_catalyst.py,sha256=DqEApmjxQufZZT4ap-IzoBmp3AX6RWMq7HXQ5kO0wsI,16468
3
+ ragaai_catalyst/dataset.py,sha256=xEun-MqTgT0JGyGB2t0BwwC6M7t1aUT7x5X12C-_4pI,7644
4
+ ragaai_catalyst/experiment.py,sha256=tvIfL3xSrPsPpwHAHIn4bnxx4e_nhynqJYx2i3UjJRQ,18850
5
+ ragaai_catalyst/prompt_manager.py,sha256=3jEcgS0JRZzjHww_ityOkq9SMvi7DQ75PlKu-17dXW4,14729
6
+ ragaai_catalyst/ragaai_catalyst.py,sha256=LJECRdCmW8DtF3Lk4wZ6lXJgsYkh6Oome45DzONyaVU,16228
6
7
  ragaai_catalyst/utils.py,sha256=TlhEFwLyRU690HvANbyoRycR3nQ67lxVUQoUOfTPYQ0,3772
7
8
  ragaai_catalyst/tracers/__init__.py,sha256=NppmJhD3sQ5R1q6teaZLS7rULj08Gb6JT8XiPRIe_B0,49
8
- ragaai_catalyst/tracers/tracer.py,sha256=hTLW1nj_TWv7yo77d8EVFv2SLYB78VE0jA0rMTrrJQQ,8299
9
+ ragaai_catalyst/tracers/tracer.py,sha256=LZQNKQF6hRfzxDu_Ljs2HBxQinjihKQoH189gIsO4w4,9171
9
10
  ragaai_catalyst/tracers/exporters/__init__.py,sha256=kVA8zp05h3phu4e-iHSlnznp_PzMRczB7LphSsZgUjg,138
10
11
  ragaai_catalyst/tracers/exporters/file_span_exporter.py,sha256=E1uSBZ8JfUBLLtkhN3lIy_AWmPcZDCInsOFhPoWP9mU,6362
11
- ragaai_catalyst/tracers/exporters/raga_exporter.py,sha256=M2O-Hi4wLzezc7WrfoBauoHGnvdQYq7dfdsLhCSHSSE,16365
12
+ ragaai_catalyst/tracers/exporters/raga_exporter.py,sha256=yxLadWzGCTr6AMh9n0U_1xNlw80aAcLE0KthktC2xMU,17487
12
13
  ragaai_catalyst/tracers/instrumentators/__init__.py,sha256=FgnMQupoRTzmVsG9YKsLQera2Pfs-AluZv8CxwavoyQ,253
13
14
  ragaai_catalyst/tracers/instrumentators/langchain.py,sha256=yMN0qVF0pUVk6R5M1vJoUXezDo1ejs4klCFRlE8x4vE,574
14
15
  ragaai_catalyst/tracers/instrumentators/llamaindex.py,sha256=SMrRlR4xM7k9HK43hakE8rkrWHxMlmtmWD-AX6TeByc,416
15
16
  ragaai_catalyst/tracers/instrumentators/openai.py,sha256=14R4KW9wQCR1xysLfsP_nxS7cqXrTPoD8En4MBAaZUU,379
16
17
  ragaai_catalyst/tracers/utils/__init__.py,sha256=KeMaZtYaTojilpLv65qH08QmpYclfpacDA0U3wg6Ybw,64
17
18
  ragaai_catalyst/tracers/utils/utils.py,sha256=ViygfJ7vZ7U0CTSA1lbxVloHp4NSlmfDzBRNCJuMhis,2374
18
- ragaai_catalyst-1.0.7b4.dist-info/METADATA,sha256=AcE3Fj9TqlB4if-kqn70Kd0NPCae_8I1_FiS0iSe0tw,5121
19
- ragaai_catalyst-1.0.7b4.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
20
- ragaai_catalyst-1.0.7b4.dist-info/top_level.txt,sha256=HpgsdRgEJMk8nqrU6qdCYk3di7MJkDL0B19lkc7dLfM,16
21
- ragaai_catalyst-1.0.7b4.dist-info/RECORD,,
19
+ ragaai_catalyst-1.0.8b2.dist-info/METADATA,sha256=io-4QAFqjIbnTZrgvjIk3PSMRfRr9P-rNpvjSwkihxY,6499
20
+ ragaai_catalyst-1.0.8b2.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
21
+ ragaai_catalyst-1.0.8b2.dist-info/top_level.txt,sha256=HpgsdRgEJMk8nqrU6qdCYk3di7MJkDL0B19lkc7dLfM,16
22
+ ragaai_catalyst-1.0.8b2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.0.0)
2
+ Generator: setuptools (74.1.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5