ragaai-catalyst 2.0.5__py3-none-any.whl → 2.0.6b1__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.
- ragaai_catalyst/__init__.py +3 -1
- ragaai_catalyst/dataset.py +49 -60
- ragaai_catalyst/evaluation.py +47 -29
- ragaai_catalyst/guard_executor.py +97 -0
- ragaai_catalyst/guardrails_manager.py +259 -0
- ragaai_catalyst/internal_api_completion.py +83 -0
- ragaai_catalyst/proxy_call.py +1 -1
- ragaai_catalyst/synthetic_data_generation.py +206 -77
- ragaai_catalyst/tracers/llamaindex_callback.py +361 -0
- ragaai_catalyst/tracers/tracer.py +62 -28
- {ragaai_catalyst-2.0.5.dist-info → ragaai_catalyst-2.0.6b1.dist-info}/METADATA +139 -72
- {ragaai_catalyst-2.0.5.dist-info → ragaai_catalyst-2.0.6b1.dist-info}/RECORD +14 -10
- {ragaai_catalyst-2.0.5.dist-info → ragaai_catalyst-2.0.6b1.dist-info}/WHEEL +1 -1
- {ragaai_catalyst-2.0.5.dist-info → ragaai_catalyst-2.0.6b1.dist-info}/top_level.txt +0 -0
ragaai_catalyst/__init__.py
CHANGED
@@ -6,6 +6,8 @@ from .dataset import Dataset
|
|
6
6
|
from .prompt_manager import PromptManager
|
7
7
|
from .evaluation import Evaluation
|
8
8
|
from .synthetic_data_generation import SyntheticDataGeneration
|
9
|
+
from .guardrails_manager import GuardrailsManager
|
10
|
+
from .guard_executor import GuardExecutor
|
9
11
|
|
10
12
|
|
11
|
-
__all__ = ["Experiment", "RagaAICatalyst", "Tracer", "PromptManager", "Evaluation","SyntheticDataGeneration"]
|
13
|
+
__all__ = ["Experiment", "RagaAICatalyst", "Tracer", "PromptManager", "Evaluation","SyntheticDataGeneration", "GuardrailsManager"]
|
ragaai_catalyst/dataset.py
CHANGED
@@ -99,82 +99,71 @@ class Dataset:
|
|
99
99
|
raise
|
100
100
|
|
101
101
|
def get_schema_mapping(self):
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
Creates a new dataset with the given `dataset_name` and `filter_list`.
|
107
|
-
|
108
|
-
Args:
|
109
|
-
dataset_name (str): The name of the dataset to be created.
|
110
|
-
filter_list (list): A list of filters to be applied to the dataset.
|
111
|
-
|
112
|
-
Returns:
|
113
|
-
str: A message indicating the success of the dataset creation and the name of the created dataset.
|
114
|
-
|
115
|
-
Raises:
|
116
|
-
None
|
117
|
-
|
118
|
-
"""
|
119
|
-
|
120
|
-
def request_trace_creation():
|
121
|
-
headers = {
|
122
|
-
"Content-Type": "application/json",
|
123
|
-
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
124
|
-
"X-Project-Name": self.project_name,
|
125
|
-
}
|
126
|
-
json_data = {
|
127
|
-
"projectName": self.project_name,
|
128
|
-
"subDatasetName": dataset_name,
|
129
|
-
"filterList": filter_list,
|
130
|
-
}
|
131
|
-
try:
|
132
|
-
response = requests.post(
|
133
|
-
f"{Dataset.BASE_URL}/v1/llm/sub-dataset",
|
134
|
-
headers=headers,
|
135
|
-
json=json_data,
|
136
|
-
timeout=Dataset.TIMEOUT,
|
137
|
-
)
|
138
|
-
response.raise_for_status()
|
139
|
-
return response
|
140
|
-
except requests.exceptions.RequestException as e:
|
141
|
-
logger.error(f"Failed to create dataset from trace: {e}")
|
142
|
-
raise
|
143
|
-
|
102
|
+
headers = {
|
103
|
+
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
104
|
+
"X-Project-Name": self.project_name,
|
105
|
+
}
|
144
106
|
try:
|
145
|
-
response =
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
107
|
+
response = requests.get(
|
108
|
+
f"{Dataset.BASE_URL}/v1/llm/schema-elements",
|
109
|
+
headers=headers,
|
110
|
+
timeout=Dataset.TIMEOUT,
|
111
|
+
)
|
112
|
+
response.raise_for_status()
|
113
|
+
response_data = response.json()["data"]["schemaElements"]
|
114
|
+
if not response.json()['success']:
|
115
|
+
raise ValueError('Unable to fetch Schema Elements for the CSV')
|
116
|
+
return response_data
|
117
|
+
except requests.exceptions.RequestException as e:
|
118
|
+
logger.error(f"Failed to get CSV schema: {e}")
|
156
119
|
raise
|
157
120
|
|
158
121
|
###################### CSV Upload APIs ###################
|
159
122
|
|
160
|
-
def
|
123
|
+
def get_dataset_columns(self, dataset_name):
|
124
|
+
list_dataset = self.list_datasets()
|
125
|
+
if dataset_name not in list_dataset:
|
126
|
+
raise ValueError(f"Dataset {dataset_name} does not exists. Please enter a valid dataset name")
|
127
|
+
|
161
128
|
headers = {
|
162
129
|
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
163
130
|
"X-Project-Name": self.project_name,
|
164
131
|
}
|
132
|
+
headers = {
|
133
|
+
'Content-Type': 'application/json',
|
134
|
+
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
135
|
+
"X-Project-Id": str(self.project_id),
|
136
|
+
}
|
137
|
+
json_data = {"size": 12, "page": "0", "projectId": str(self.project_id), "search": ""}
|
138
|
+
try:
|
139
|
+
response = requests.post(
|
140
|
+
f"{Dataset.BASE_URL}/v2/llm/dataset",
|
141
|
+
headers=headers,
|
142
|
+
json=json_data,
|
143
|
+
timeout=Dataset.TIMEOUT,
|
144
|
+
)
|
145
|
+
response.raise_for_status()
|
146
|
+
datasets = response.json()["data"]["content"]
|
147
|
+
dataset_id = [dataset["id"] for dataset in datasets if dataset["name"]==dataset_name][0]
|
148
|
+
except requests.exceptions.RequestException as e:
|
149
|
+
logger.error(f"Failed to list datasets: {e}")
|
150
|
+
raise
|
151
|
+
|
165
152
|
try:
|
166
153
|
response = requests.get(
|
167
|
-
f"{Dataset.BASE_URL}/
|
154
|
+
f"{Dataset.BASE_URL}/v2/llm/dataset/{dataset_id}?initialCols=0",
|
168
155
|
headers=headers,
|
169
156
|
timeout=Dataset.TIMEOUT,
|
170
157
|
)
|
171
158
|
response.raise_for_status()
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
159
|
+
dataset_columns = response.json()["data"]["datasetColumnsResponses"]
|
160
|
+
dataset_columns = [item["displayName"] for item in dataset_columns]
|
161
|
+
dataset_columns = [data for data in dataset_columns if not data.startswith('_')]
|
162
|
+
if not response.json()['success']:
|
163
|
+
raise ValueError('Unable to fetch details of for the CSV')
|
164
|
+
return dataset_columns
|
176
165
|
except requests.exceptions.RequestException as e:
|
177
|
-
logger.error(f"Failed to get CSV
|
166
|
+
logger.error(f"Failed to get CSV columns: {e}")
|
178
167
|
raise
|
179
168
|
|
180
169
|
def create_from_csv(self, csv_path, dataset_name, schema_mapping):
|
ragaai_catalyst/evaluation.py
CHANGED
@@ -97,14 +97,45 @@ class Evaluation:
|
|
97
97
|
logger.error(f"An unexpected error occurred: {e}")
|
98
98
|
return []
|
99
99
|
|
100
|
-
def
|
100
|
+
def _get_dataset_id_based_on_dataset_type(self, metric_to_evaluate):
|
101
|
+
try:
|
102
|
+
headers = {
|
103
|
+
'Content-Type': 'application/json',
|
104
|
+
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
105
|
+
"X-Project-Id": str(self.project_id),
|
106
|
+
}
|
107
|
+
json_data = {"size": 12, "page": "0", "projectId": str(self.project_id), "search": ""}
|
108
|
+
response = requests.post(
|
109
|
+
f"{self.base_url}/v2/llm/dataset",
|
110
|
+
headers=headers,
|
111
|
+
json=json_data,
|
112
|
+
timeout=self.timeout,
|
113
|
+
)
|
114
|
+
|
115
|
+
response.raise_for_status()
|
116
|
+
datasets_content = response.json()["data"]["content"]
|
117
|
+
dataset = [dataset for dataset in datasets_content if dataset["name"]==self.dataset_name][0]
|
118
|
+
if (dataset["datasetType"]=="prompt" and metric_to_evaluate=="prompt") or (dataset["datasetType"]=="chat" and metric_to_evaluate=="chat") or dataset["datasetType"]==None:
|
119
|
+
return dataset["id"]
|
120
|
+
else:
|
121
|
+
return dataset["derivedDatasetId"]
|
122
|
+
except requests.exceptions.RequestException as e:
|
123
|
+
logger.error(f"Failed to retrieve dataset list: {e}")
|
124
|
+
raise
|
125
|
+
|
126
|
+
|
127
|
+
def _get_dataset_schema(self, metric_to_evaluate=None):
|
128
|
+
#this dataset_id is based on which type of metric_to_evaluate
|
129
|
+
data_set_id=self._get_dataset_id_based_on_dataset_type(metric_to_evaluate)
|
130
|
+
self.dataset_id=data_set_id
|
131
|
+
|
101
132
|
headers = {
|
102
133
|
"Authorization": f"Bearer {os.getenv('RAGAAI_CATALYST_TOKEN')}",
|
103
134
|
'Content-Type': 'application/json',
|
104
135
|
'X-Project-Id': str(self.project_id),
|
105
136
|
}
|
106
137
|
data = {
|
107
|
-
"datasetId": str(
|
138
|
+
"datasetId": str(data_set_id),
|
108
139
|
"fields": [],
|
109
140
|
"rowFilterList": []
|
110
141
|
}
|
@@ -129,29 +160,9 @@ class Evaluation:
|
|
129
160
|
logger.error(f"An unexpected error occurred: {e}")
|
130
161
|
return {}
|
131
162
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
dataset_schema = self._get_dataset_schema()
|
136
|
-
variableName = None
|
137
|
-
for column in dataset_schema:
|
138
|
-
columnName = column["columnType"]
|
139
|
-
displayName = column["displayName"]
|
140
|
-
# print(columnName, displayName)
|
141
|
-
if "".join(columnName.split("_")).lower() == schemaName.lower():
|
142
|
-
variableName = displayName
|
143
|
-
break
|
144
|
-
return variableName
|
145
|
-
# print(variableName)
|
146
|
-
# if variableName:
|
147
|
-
# return variableName
|
148
|
-
# else:
|
149
|
-
# raise ValueError(f"'{schemaName}' column is required for {metric_name} metric evaluation, but not found in dataset")
|
150
|
-
|
151
|
-
|
152
|
-
def _get_variablename_from_user_schema_mapping(self, schemaName, metric_name, schema_mapping):
|
153
|
-
# pdb.set_trace()
|
154
|
-
user_dataset_schema = self._get_dataset_schema()
|
163
|
+
|
164
|
+
def _get_variablename_from_user_schema_mapping(self, schemaName, metric_name, schema_mapping, metric_to_evaluate):
|
165
|
+
user_dataset_schema = self._get_dataset_schema(metric_to_evaluate)
|
155
166
|
user_dataset_columns = [item["displayName"] for item in user_dataset_schema]
|
156
167
|
variableName = None
|
157
168
|
for key, val in schema_mapping.items():
|
@@ -159,7 +170,7 @@ class Evaluation:
|
|
159
170
|
if key in user_dataset_columns:
|
160
171
|
variableName=key
|
161
172
|
else:
|
162
|
-
raise ValueError(f"Column '{key}' is not present in {self.dataset_name}")
|
173
|
+
raise ValueError(f"Column '{key}' is not present in '{self.dataset_name}' dataset")
|
163
174
|
if variableName:
|
164
175
|
return variableName
|
165
176
|
else:
|
@@ -172,10 +183,17 @@ class Evaluation:
|
|
172
183
|
for schema in metrics_schema:
|
173
184
|
if schema["name"]==metric_name:
|
174
185
|
requiredFields = schema["config"]["requiredFields"]
|
186
|
+
|
187
|
+
#this is added to check if "Chat" column is required for metric evaluation
|
188
|
+
required_variables = [_["name"].lower() for _ in requiredFields]
|
189
|
+
if "chat" in required_variables:
|
190
|
+
metric_to_evaluate = "chat"
|
191
|
+
else:
|
192
|
+
metric_to_evaluate = "prompt"
|
193
|
+
|
175
194
|
for field in requiredFields:
|
176
195
|
schemaName = field["name"]
|
177
|
-
|
178
|
-
variableName = self._get_variablename_from_user_schema_mapping(schemaName.lower(), metric_name, schema_mapping)
|
196
|
+
variableName = self._get_variablename_from_user_schema_mapping(schemaName.lower(), metric_name, schema_mapping, metric_to_evaluate)
|
179
197
|
mapping.append({"schemaName": schemaName, "variableName": variableName})
|
180
198
|
return mapping
|
181
199
|
|
@@ -223,7 +241,6 @@ class Evaluation:
|
|
223
241
|
return []
|
224
242
|
|
225
243
|
def _update_base_json(self, metrics):
|
226
|
-
metric_schema_mapping = {"datasetId":self.dataset_id}
|
227
244
|
metrics_schema_response = self._get_metrics_schema_response()
|
228
245
|
sub_providers = ["openai","azure","gemini","groq"]
|
229
246
|
metricParams = []
|
@@ -253,6 +270,7 @@ class Evaluation:
|
|
253
270
|
mappings = self._get_mapping(metric["name"], metrics_schema_response, metric["schema_mapping"])
|
254
271
|
base_json["metricSpec"]["config"]["mappings"] = mappings
|
255
272
|
metricParams.append(base_json)
|
273
|
+
metric_schema_mapping = {"datasetId":self.dataset_id}
|
256
274
|
metric_schema_mapping["metricParams"] = metricParams
|
257
275
|
return metric_schema_mapping
|
258
276
|
|
@@ -0,0 +1,97 @@
|
|
1
|
+
import litellm
|
2
|
+
import json
|
3
|
+
import requests
|
4
|
+
import os
|
5
|
+
import logging
|
6
|
+
logger = logging.getLogger('LiteLLM')
|
7
|
+
logger.setLevel(logging.ERROR)
|
8
|
+
|
9
|
+
class GuardExecutor:
|
10
|
+
|
11
|
+
def __init__(self,id,guard_manager,field_map={}):
|
12
|
+
self.deployment_id = id
|
13
|
+
self.field_map = field_map
|
14
|
+
self.guard_manager = guard_manager
|
15
|
+
self.deployment_details = self.guard_manager.get_deployment(id)
|
16
|
+
if not self.deployment_details:
|
17
|
+
raise ValueError('Error in getting deployment details')
|
18
|
+
self.base_url = guard_manager.base_url
|
19
|
+
for key in field_map.keys():
|
20
|
+
if key not in ['prompt','context','response','instruction']:
|
21
|
+
print('Keys in field map should be in ["prompt","context","response","instruction"]')
|
22
|
+
|
23
|
+
def execute_deployment(self,payload):
|
24
|
+
api = self.base_url + f'/guardrail/deployment/{self.deployment_id}/ingest'
|
25
|
+
|
26
|
+
payload = json.dumps(payload)
|
27
|
+
headers = {
|
28
|
+
'x-project-id': str(self.guard_manager.project_id),
|
29
|
+
'Content-Type': 'application/json',
|
30
|
+
'Authorization': f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}'
|
31
|
+
}
|
32
|
+
try:
|
33
|
+
response = requests.request("POST", api, headers=headers, data=payload,timeout=self.guard_manager.timeout)
|
34
|
+
except Exception as e:
|
35
|
+
print('Failed running guardrail: ',str(e))
|
36
|
+
return None
|
37
|
+
if response.status_code!=200:
|
38
|
+
print('Error in running deployment ',response.json()['message'])
|
39
|
+
if response.json()['success']:
|
40
|
+
return response.json()
|
41
|
+
else:
|
42
|
+
print(response.json()['message'])
|
43
|
+
return None
|
44
|
+
|
45
|
+
def llm_executor(self,messages,model_params,llm_caller):
|
46
|
+
if llm_caller == 'litellm':
|
47
|
+
model_params['messages'] = messages
|
48
|
+
response = litellm.completion(**model_params)
|
49
|
+
return response
|
50
|
+
else:
|
51
|
+
print(f"{llm_caller} not supported currently, use litellm as llm caller")
|
52
|
+
|
53
|
+
|
54
|
+
def __call__(self,messages,prompt_params,model_params,llm_caller='litellm'):
|
55
|
+
for key in self.field_map:
|
56
|
+
if key not in ['prompt','response']:
|
57
|
+
if self.field_map[key] not in prompt_params:
|
58
|
+
raise ValueError(f'{key} added as field map but not passed as prompt parameter')
|
59
|
+
context_var = self.field_map.get('context',None)
|
60
|
+
prompt = None
|
61
|
+
for msg in messages:
|
62
|
+
if 'role' in msg:
|
63
|
+
if msg['role'] == 'user':
|
64
|
+
prompt = msg['content']
|
65
|
+
if not context_var:
|
66
|
+
msg['content'] += '\n' + prompt_params[context_var]
|
67
|
+
doc = dict()
|
68
|
+
doc['prompt'] = prompt
|
69
|
+
doc['context'] = prompt_params[context_var]
|
70
|
+
|
71
|
+
# inactive the guardrails that needs Response variable
|
72
|
+
#deployment_response = self.execute_deployment(doc)
|
73
|
+
|
74
|
+
# activate only guardrails that require response
|
75
|
+
try:
|
76
|
+
llm_response = self.llm_executor(messages,model_params,llm_caller)
|
77
|
+
except Exception as e:
|
78
|
+
print('Error in running llm:',str(e))
|
79
|
+
return None
|
80
|
+
doc['response'] = llm_response['choices'][0].message.content
|
81
|
+
if 'instruction' in self.field_map:
|
82
|
+
instruction = prompt_params[self.field_map['instruction']]
|
83
|
+
doc['instruction'] = instruction
|
84
|
+
response = self.execute_deployment(doc)
|
85
|
+
if response and response['data']['status'] == 'FAIL':
|
86
|
+
print('Guardrail deployment run retured failed status, replacing with alternate response')
|
87
|
+
return response['data']['alternateResponse'],llm_response,response
|
88
|
+
else:
|
89
|
+
return None,llm_response,response
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
@@ -0,0 +1,259 @@
|
|
1
|
+
import requests
|
2
|
+
import json
|
3
|
+
import os
|
4
|
+
from .ragaai_catalyst import RagaAICatalyst
|
5
|
+
|
6
|
+
|
7
|
+
class GuardrailsManager:
|
8
|
+
def __init__(self, project_name):
|
9
|
+
"""
|
10
|
+
Initialize the GuardrailsManager with the given project name.
|
11
|
+
|
12
|
+
:param project_name: The name of the project to manage guardrails for.
|
13
|
+
"""
|
14
|
+
self.project_name = project_name
|
15
|
+
self.timeout = 10
|
16
|
+
self.num_projects = 100
|
17
|
+
self.deployment_name = "NA"
|
18
|
+
self.deployment_id = "NA"
|
19
|
+
self.base_url = f"{RagaAICatalyst.BASE_URL}"
|
20
|
+
list_projects, project_name_with_id = self._get_project_list()
|
21
|
+
if project_name not in list_projects:
|
22
|
+
raise ValueError(f"Project '{self.project_name}' does not exists")
|
23
|
+
|
24
|
+
self.project_id = [_["id"] for _ in project_name_with_id if _["name"]==self.project_name][0]
|
25
|
+
|
26
|
+
|
27
|
+
def _get_project_list(self):
|
28
|
+
"""
|
29
|
+
Retrieve the list of projects and their IDs from the API.
|
30
|
+
|
31
|
+
:return: A tuple containing a list of project names and a list of dictionaries with project IDs and names.
|
32
|
+
"""
|
33
|
+
headers = {'Authorization': f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}'}
|
34
|
+
response = requests.request("GET", f"{self.base_url}/v2/llm/projects?size=12&page=0", headers=headers, timeout=self.timeout)
|
35
|
+
project_content = response.json()["data"]["content"]
|
36
|
+
list_project = [_["name"] for _ in project_content]
|
37
|
+
project_name_with_id = [{"id": _["id"], "name": _["name"]} for _ in project_content]
|
38
|
+
return list_project, project_name_with_id
|
39
|
+
|
40
|
+
|
41
|
+
def list_deployment_ids(self):
|
42
|
+
"""
|
43
|
+
List all deployment IDs and their names for the current project.
|
44
|
+
|
45
|
+
:return: A list of dictionaries containing deployment IDs and names.
|
46
|
+
"""
|
47
|
+
payload = {}
|
48
|
+
headers = {
|
49
|
+
'Authorization': f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
|
50
|
+
'X-Project-Id': str(self.project_id)
|
51
|
+
}
|
52
|
+
response = requests.request("GET", f"{self.base_url}/guardrail/deployment?size={self.num_projects}&page=0&sort=lastUsedAt,desc", headers=headers, data=payload, timeout=self.timeout)
|
53
|
+
deployment_ids_content = response.json()["data"]["content"]
|
54
|
+
deployment_ids_content = [{"id": _["id"], "name": _["name"]} for _ in deployment_ids_content]
|
55
|
+
return deployment_ids_content
|
56
|
+
|
57
|
+
|
58
|
+
def get_deployment(self, deployment_id):
|
59
|
+
"""
|
60
|
+
Get details of a specific deployment ID, including its name and guardrails.
|
61
|
+
|
62
|
+
:param deployment_id: The ID of the deployment to retrieve details for.
|
63
|
+
:return: A dictionary containing the deployment name and a list of guardrails.
|
64
|
+
"""
|
65
|
+
payload = {}
|
66
|
+
headers = {
|
67
|
+
'Authorization': f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
|
68
|
+
'X-Project-Id': str(self.project_id)
|
69
|
+
}
|
70
|
+
response = requests.request("GET", f"{self.base_url}/guardrail/deployment/{deployment_id}", headers=headers, data=payload, timeout=self.timeout)
|
71
|
+
if response.json()['success']:
|
72
|
+
return response.json()
|
73
|
+
else:
|
74
|
+
print('Error in retrieving deployment details:',response.json()['message'])
|
75
|
+
return None
|
76
|
+
|
77
|
+
|
78
|
+
def list_guardrails(self):
|
79
|
+
"""
|
80
|
+
List all available guardrails for the current project.
|
81
|
+
|
82
|
+
:return: A list of guardrail names.
|
83
|
+
"""
|
84
|
+
payload = {}
|
85
|
+
headers = {
|
86
|
+
'Authorization': f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
|
87
|
+
'X-Project-Id': str(self.project_id)
|
88
|
+
}
|
89
|
+
response = requests.request("GET", f"{self.base_url}/v1/llm/llm-metrics?category=Guardrail", headers=headers, data=payload, timeout=self.timeout)
|
90
|
+
list_guardrails_content = response.json()["data"]["metrics"]
|
91
|
+
list_guardrails = [_["name"] for _ in list_guardrails_content]
|
92
|
+
return list_guardrails
|
93
|
+
|
94
|
+
|
95
|
+
def list_fail_condition(self):
|
96
|
+
"""
|
97
|
+
List all fail conditions for the current project's deployments.
|
98
|
+
|
99
|
+
:return: A list of fail conditions.
|
100
|
+
"""
|
101
|
+
payload = {}
|
102
|
+
headers = {
|
103
|
+
'Authorization': f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
|
104
|
+
'X-Project-Id': str(self.project_id)
|
105
|
+
}
|
106
|
+
response = requests.request("GET", f"{self.base_url}/guardrail/deployment/configurations", headers=headers, data=payload, timeout=self.timeout)
|
107
|
+
return response.json()["data"]
|
108
|
+
|
109
|
+
|
110
|
+
def create_deployment(self, deployment_name):
|
111
|
+
"""
|
112
|
+
Create a new deployment ID with the given name.
|
113
|
+
|
114
|
+
:param deployment_name: The name of the new deployment.
|
115
|
+
:raises ValueError: If a deployment with the given name already exists.
|
116
|
+
"""
|
117
|
+
self.deployment_name = deployment_name
|
118
|
+
list_deployment_ids = self.list_deployment_ids()
|
119
|
+
list_deployment_names = [_["name"] for _ in list_deployment_ids]
|
120
|
+
if deployment_name in list_deployment_names:
|
121
|
+
raise ValueError(f"Deployment with '{deployment_name}' already exists, choose a unique name")
|
122
|
+
|
123
|
+
payload = json.dumps({"name": str(deployment_name)})
|
124
|
+
headers = {
|
125
|
+
'Authorization': f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
|
126
|
+
'Content-Type': 'application/json',
|
127
|
+
'X-Project-Id': str(self.project_id)
|
128
|
+
}
|
129
|
+
response = requests.request("POST", f"{self.base_url}/guardrail/deployment", headers=headers, data=payload, timeout=self.timeout)
|
130
|
+
if response.status_code == 409:
|
131
|
+
raise ValueError(f"Data with '{deployment_name}' already exists, choose a unique name")
|
132
|
+
if response.json()["success"]:
|
133
|
+
print(response.json()["message"])
|
134
|
+
deployment_ids = self.list_deployment_ids()
|
135
|
+
self.deployment_id = [_["id"] for _ in deployment_ids if _["name"]==self.deployment_name][0]
|
136
|
+
return self.deployment_id
|
137
|
+
else:
|
138
|
+
print(response)
|
139
|
+
|
140
|
+
|
141
|
+
def add_guardrails(self, deployment_id, guardrails, guardrails_config={}):
|
142
|
+
"""
|
143
|
+
Add guardrails to the current deployment.
|
144
|
+
|
145
|
+
:param guardrails: A list of guardrails to add.
|
146
|
+
:param guardrails_config: Configuration settings for the guardrails.
|
147
|
+
:raises ValueError: If a guardrail name or type is invalid.
|
148
|
+
"""
|
149
|
+
# Checking if guardrails names given already exist or not
|
150
|
+
self.deployment_id = deployment_id
|
151
|
+
deployment_details = self.get_deployment(self.deployment_id)
|
152
|
+
if not deployment_details:
|
153
|
+
return None
|
154
|
+
deployment_id_name = deployment_details["data"]["name"]
|
155
|
+
deployment_id_guardrails = deployment_details["data"]["guardrailsResponse"]
|
156
|
+
guardrails_type_name_exists = [{_['metricSpec']["name"]:_['metricSpec']["displayName"]} for _ in deployment_id_guardrails]
|
157
|
+
guardrails_type_name_exists = [list(d.values())[0] for d in guardrails_type_name_exists]
|
158
|
+
user_guardrails_name_list = [_["name"] for _ in guardrails]
|
159
|
+
for g_name in user_guardrails_name_list:
|
160
|
+
if g_name in guardrails_type_name_exists:
|
161
|
+
raise ValueError(f"Guardrail with '{g_name} already exists, choose a unique name'")
|
162
|
+
# Checking if guardrails type is correct or not
|
163
|
+
available_guardrails_list = self.list_guardrails()
|
164
|
+
user_guardrails_type_list = [_["name"] for _ in guardrails]
|
165
|
+
for g_type in user_guardrails_type_list:
|
166
|
+
if g_type not in available_guardrails_list:
|
167
|
+
raise ValueError(f"Guardrail type '{g_type} does not exists, choose a correct type'")
|
168
|
+
|
169
|
+
payload = self._get_guardrail_config_payload(guardrails_config)
|
170
|
+
payload["guardrails"] = self._get_guardrail_list_payload(guardrails)
|
171
|
+
payload = json.dumps(payload)
|
172
|
+
headers = {
|
173
|
+
'Authorization': f'Bearer {os.getenv("RAGAAI_CATALYST_TOKEN")}',
|
174
|
+
'Content-Type': 'application/json',
|
175
|
+
'X-Project-Id': str(self.project_id)
|
176
|
+
}
|
177
|
+
response = requests.request("POST", f"{self.base_url}/guardrail/deployment/{str(self.deployment_id)}/configure", headers=headers, data=payload)
|
178
|
+
if response.json()["success"]:
|
179
|
+
print(response.json()["message"])
|
180
|
+
else:
|
181
|
+
print('Error updating guardrail ',response.json()['message'])
|
182
|
+
|
183
|
+
def _get_guardrail_config_payload(self, guardrails_config):
|
184
|
+
"""
|
185
|
+
Construct the payload for guardrail configuration.
|
186
|
+
|
187
|
+
:param guardrails_config: Configuration settings for the guardrails.
|
188
|
+
:return: A dictionary representing the guardrail configuration payload.
|
189
|
+
"""
|
190
|
+
data = {
|
191
|
+
"isActive": guardrails_config.get("isActive",False),
|
192
|
+
"guardrailFailConditions": guardrails_config.get("guardrailFailConditions",["FAIL"]),
|
193
|
+
"deploymentFailCondition": guardrails_config.get("deploymentFailCondition","ONE_FAIL"),
|
194
|
+
"failAction": {
|
195
|
+
"action": "ALTERNATE_RESPONSE",
|
196
|
+
"args": f'{{\"alternateResponse\": \"{guardrails_config.get("alternateResponse","This is the Alternate Response")}\"}}'
|
197
|
+
},
|
198
|
+
"guardrails" : []
|
199
|
+
}
|
200
|
+
return data
|
201
|
+
|
202
|
+
def _get_guardrail_list_payload(self, guardrails):
|
203
|
+
"""
|
204
|
+
Construct the payload for a list of guardrails.
|
205
|
+
|
206
|
+
:param guardrails: A list of guardrails to include in the payload.
|
207
|
+
:return: A list of dictionaries representing each guardrail's data.
|
208
|
+
"""
|
209
|
+
guardrails_list_payload = []
|
210
|
+
for guardrail in guardrails:
|
211
|
+
guardrails_list_payload.append(self._get_one_guardrail_data(guardrail))
|
212
|
+
return guardrails_list_payload
|
213
|
+
|
214
|
+
def _get_one_guardrail_data(self, guardrail):
|
215
|
+
"""
|
216
|
+
Construct the data for a single guardrail.
|
217
|
+
|
218
|
+
:param guardrail: A dictionary containing the guardrail's attributes.
|
219
|
+
:return: A dictionary representing the guardrail's data.
|
220
|
+
"""
|
221
|
+
if 'config' in guardrail:
|
222
|
+
if 'mappings' in guardrail.get('config'):
|
223
|
+
for mapping in guardrail.get('config',{}).get('mappings',{}):
|
224
|
+
if mapping['schemaName'] not in ['Text','Prompt','Context','Response']:
|
225
|
+
raise(ValueError('Invalid schemaName in guardrail mapping schema'))
|
226
|
+
if mapping['variableName'] not in ['Instruction','Prompt','Context','Response']:
|
227
|
+
raise(ValueError('Invalid variableName in guardrail mapping schema'))
|
228
|
+
if 'model' in guardrail.get('config'):
|
229
|
+
if guardrail.get('config',{}).get('model','') not in ['gpt-4o-mini','gpt-4o','gpt-4-turbo']:
|
230
|
+
raise(ValueError('Invalid model name in guardrail model schema'))
|
231
|
+
if 'params' not in guardrail.get('config'):
|
232
|
+
guardrail['config']['params'] = {
|
233
|
+
"isActive": {"value": False},
|
234
|
+
"isHighRisk": {"value": False},
|
235
|
+
"threshold": {"lt": 1}
|
236
|
+
}
|
237
|
+
|
238
|
+
|
239
|
+
data = {
|
240
|
+
"displayName": guardrail["displayName"],
|
241
|
+
"name": guardrail["name"],
|
242
|
+
"config": guardrail.get("config", {})
|
243
|
+
}
|
244
|
+
'''
|
245
|
+
if "lte" in guardrail["threshold"]:
|
246
|
+
data["threshold"]["lte"] = guardrail["threshold"]["lte"]
|
247
|
+
elif "gte" in guardrail["threshold"]:
|
248
|
+
data["threshold"]["gte"] = guardrail["threshold"]["gte"]
|
249
|
+
elif "eq" in guardrail["threshold"]:
|
250
|
+
data["threshold"]["eq"] = guardrail["threshold"]["eq"]
|
251
|
+
else:
|
252
|
+
data["threshold"]["gte"] = 0.0'''
|
253
|
+
return data
|
254
|
+
|
255
|
+
|
256
|
+
def _run(self, **kwargs):
|
257
|
+
"""
|
258
|
+
Execute the guardrail checks with the provided variables.
|
259
|
+
"""
|